001/*
002 *  Copyright 2017 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.thesaurus.right;
017
018import java.util.Map;
019import java.util.Set;
020
021import org.apache.avalon.framework.context.Context;
022import org.apache.avalon.framework.context.ContextException;
023import org.apache.avalon.framework.context.Contextualizable;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.cocoon.components.ContextHelper;
027import org.apache.cocoon.environment.Request;
028import org.apache.commons.collections.MapUtils;
029import org.apache.commons.lang3.StringUtils;
030
031import org.ametys.cms.content.archive.ArchiveConstants;
032import org.ametys.cms.contenttype.ContentTypesHelper;
033import org.ametys.cms.repository.Content;
034import org.ametys.cms.rights.ContentTypeAccessController;
035import org.ametys.core.group.GroupIdentity;
036import org.ametys.core.right.AccessController;
037import org.ametys.core.right.AccessController.Permission.PermissionType;
038import org.ametys.core.right.AccessExplanation;
039import org.ametys.core.right.Profile;
040import org.ametys.core.right.RightsException;
041import org.ametys.core.right.RightsExtensionPoint;
042import org.ametys.core.user.UserIdentity;
043import org.ametys.plugins.core.impl.right.AbstractProfileStorageBasedAccessController;
044import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
045import org.ametys.plugins.thesaurus.ThesaurusDAO;
046import org.ametys.runtime.i18n.I18nizableText;
047import org.ametys.runtime.i18n.I18nizableTextParameter;
048
049/**
050 * {@link AccessController} for a thesaurus objects. The rights are checked on '/cms' context.
051 * Read access is allowed to any connected user.
052 */
053public class ThesaurusAccessController extends AbstractProfileStorageBasedAccessController implements Contextualizable
054{
055    /** Right to edit thesaurus term */
056    public static final String THESAURUS_RIGHTS_EDIT_TERM = "Thesaurus_Rights_EditTerm";
057
058    /** The right context for thesaurus */
059    private static final String __APPLICATION_RIGHT_CONTEXT = "/cms";
060    
061    /** The right extension point */
062    protected RightsExtensionPoint _rightEP;
063    
064    private ContentTypesHelper _contentTypeHelper;
065
066    private Context _context;
067    
068    @Override
069    public void service(ServiceManager manager) throws ServiceException
070    {
071        super.service(manager);
072        _contentTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
073        _rightEP = (RightsExtensionPoint) manager.lookup(RightsExtensionPoint.ROLE);
074    }
075    
076    public void contextualize(Context context) throws ContextException
077    {
078        _context = context;
079    }
080    
081    public boolean supports(Object object)
082    {
083        Request request = ContextHelper.getRequest(_context);
084        String currentWorkspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
085        
086        if (ArchiveConstants.ARCHIVE_WORKSPACE.equals(currentWorkspace))
087        {
088            return false;
089        }
090        
091        return object instanceof Content && _contentTypeHelper.isInstanceOf((Content) object, ThesaurusDAO.MICROTHESAURUS_ABSTRACT_CONTENT_TYPE);
092    }
093    
094    @Override
095    protected Object _convertContext(Object initialContext)
096    {
097        String siteName = _getSiteName();
098        return __APPLICATION_RIGHT_CONTEXT + (StringUtils.isNoneEmpty(siteName) ? "/" + siteName : "");
099    }
100    
101    /**
102     * Convert the asked right id to the real right to check
103     * @param rightId The asked right id
104     * @return the right to check
105     */
106    protected String _convertRightId(String rightId)
107    {
108        return THESAURUS_RIGHTS_EDIT_TERM;
109    }
110
111    @Override
112    public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object)
113    {
114        return super.getPermission(user, userGroups, _convertRightId(rightId), object);
115    }
116    
117    @Override
118    public AccessExplanation explainPermission(UserIdentity user, Set<GroupIdentity> groups, String rightId, Object object)
119    {
120        return super.explainPermission(user, groups, _convertRightId(rightId), object);
121    }
122    
123    @Override
124    protected I18nizableText _getExplanationI18nText(PermissionDetails details)
125    {
126        return new I18nizableText("plugin.thesaurus", _getAccessExplanationI18nKey("PLUGINS_THESAURUS_ACCESS_CONTROLLER_", details), _getExplanationI18nParams(details));
127    }
128    
129    @Override
130    protected Map<String, I18nizableTextParameter> _getExplanationI18nParams(PermissionDetails details)
131    {
132        Map<String, I18nizableTextParameter> i18nParams = super._getExplanationI18nParams(details);
133        i18nParams.put("right", _rightEP.getExtension(_convertRightId(null)).getLabel());
134        return i18nParams;
135    }
136    
137    @Override
138    protected I18nizableText getObjectLabelForExplanation(Object object) throws RightsException
139    {
140        return new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_EXPLANATION_CONTEXT_LABEL");
141    }
142
143    @Override
144    public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
145    {
146        return AccessResult.ANY_CONNECTED_ALLOWED;
147    }
148    
149    @Override
150    public AccessExplanation explainReadAccessPermission(UserIdentity user, Set<GroupIdentity> groups, Object object)
151    {
152        return new AccessExplanation(
153                getId(),
154                getReadAccessPermission(user, groups, object),
155                new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_READ_ACCESS_EXPLANATION", Map.of("title", new I18nizableText(((Content) object).getTitle())))
156            );
157    }
158
159    @Override
160    public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
161    {
162        return MapUtils.EMPTY_MAP;
163    }
164
165    @Override
166    public AccessResult getPermissionForAnonymous(String rightId, Object object)
167    {
168        return AccessResult.ANONYMOUS_DENIED;
169    }
170    
171    @Override
172    public AccessExplanation explainPermissionForAnonymous(String rightId, Object object)
173    {
174        return new AccessExplanation(
175                getId(),
176                getPermissionForAnonymous(rightId, object),
177                new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_ANONYMOUS_PERMISSION_EXPLANATION", Map.of("title", new I18nizableText(((Content) object).getTitle())))
178            );
179    }
180
181    @Override
182    public AccessResult getReadAccessPermissionForAnonymous(Object object)
183    {
184        return AccessResult.ANONYMOUS_DENIED;
185    }
186    
187    @Override
188    public AccessExplanation explainReadAccessPermissionForAnonymous(Object object)
189    {
190        return new AccessExplanation(
191                getId(),
192                getReadAccessPermissionForAnonymous(object),
193                new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_ANONYMOUS_PERMISSION_EXPLANATION", Map.of("title", new I18nizableText(((Content) object).getTitle())))
194            );
195    }
196
197    @Override
198    public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object)
199    {
200        return AccessResult.ANY_CONNECTED_DENIED;
201    }
202    
203    @Override
204    public AccessExplanation explainPermissionForAnyConnectedUser(String rightId, Object object)
205    {
206        return new AccessExplanation(
207                getId(),
208                getPermissionForAnyConnectedUser(rightId, object),
209                new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_ANY_CONNECTED_PERMISSION_EXPLANATION")
210            );
211    }
212
213    @Override
214    public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object)
215    {
216        return AccessResult.ANY_CONNECTED_ALLOWED;
217    }
218    
219    @Override
220    public AccessExplanation explainReadAccessPermissionForAnyConnectedUser(Object object)
221    {
222        return new AccessExplanation(
223                getId(),
224                getReadAccessPermissionForAnyConnectedUser(object),
225                new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_READ_ACCESS_EXPLANATION")
226            );
227    }
228
229    @Override
230    public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object)
231    {
232        return MapUtils.EMPTY_MAP;
233    }
234
235    @Override
236    public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object)
237    {
238        return MapUtils.EMPTY_MAP;
239    }
240    
241    @Override
242    public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object)
243    {
244        return MapUtils.EMPTY_MAP;
245    }
246
247    @Override
248    public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object)
249    {
250        return MapUtils.EMPTY_MAP;
251    }
252    
253    @Override
254    protected boolean ignoreOnHasAnyPermission()
255    {
256        return true;
257    }
258    
259    @Override
260    protected Set< ? extends Object> _convertWorkspaceToRootRightContexts(Set<Object> workspacesContexts)
261    {
262        if (workspacesContexts.contains("/cms"))
263        {
264            // the implementation of _convertContext doesn't depends on the context
265            return Set.of(_convertContext(null));
266        }
267        return null;
268    }
269    
270    // FIXME To remove https://issues.ametys.org/browse/THES-86
271    private String _getSiteName()
272    {
273        Request request = ContextHelper.getRequest(_context);
274        String siteName = request.getParameter("siteName");
275        
276        if (siteName == null)
277        {
278            siteName = (String) request.getAttribute("siteName");
279        }
280        return siteName;
281    }
282
283    @Override
284    public Map<ExplanationObject, Map<Permission, AccessExplanation>> explainAllPermissions(UserIdentity identity, Set<GroupIdentity> groups, Set<Object> workspacesContexts)
285    {
286        AccessExplanation explanation = explainPermission(identity, groups, null, null);
287        if (explanation.accessResult() != AccessResult.UNKNOWN)
288        {
289            return Map.of(
290                    getExplanationObject("thesaurus"),
291                    Map.of(new Permission(PermissionType.ALL_RIGHTS, null), explanation)
292                );
293        }
294        return Map.of();
295    }
296    
297    public I18nizableText getObjectLabel(Object object) throws RightsException
298    {
299        return new I18nizableText("plugin.thesaurus", "PLUGINS_THESAURUS_ACCESS_CONTROLLER_ALL_CONTEXT");
300    }
301    
302    public I18nizableText getObjectCategory(Object object)
303    {
304        return ContentTypeAccessController.REFERENCE_TABLE_CONTEXT_CATEGORY;
305    }
306    
307    public int getObjectPriority(Object object)
308    {
309        // after root but before the content type
310        return 5;
311    }
312    
313    @Override
314    public Map<ExplanationObject, AccessExplanation> explainAllProfileUsesForAnonymousOnWorkspaces(String profileId, Set<Object> workspacesContexts)
315    {
316        Profile profile = _rightProfileDAO.getProfile(profileId);
317        if (profile != null
318            && _rightProfileDAO.getRights(profile).contains(THESAURUS_RIGHTS_EDIT_TERM))
319        {
320            return super.explainAllProfileUsesForAnonymousOnWorkspaces(profileId, workspacesContexts);
321        }
322        return Map.of();
323    }
324    
325    @Override
326    public Map<ExplanationObject, AccessExplanation> explainAllProfileUsesForAnyConnectedOnWorkspaces(String profileId, Set<Object> workspacesContexts)
327    {
328        Profile profile = _rightProfileDAO.getProfile(profileId);
329        if (profile != null
330            && _rightProfileDAO.getRights(profile).contains(THESAURUS_RIGHTS_EDIT_TERM))
331        {
332            return super.explainAllProfileUsesForAnyConnectedOnWorkspaces(profileId, workspacesContexts);
333        }
334        return Map.of();
335    }
336    
337    @Override
338    public Map<ExplanationObject, Map<GroupIdentity, AccessExplanation>> explainAllProfileUsesOnWorkspacesByGroups(String profileId, Set<Object> workspacesContexts)
339    {
340        Profile profile = _rightProfileDAO.getProfile(profileId);
341        if (profile != null
342            && _rightProfileDAO.getRights(profile).contains(THESAURUS_RIGHTS_EDIT_TERM))
343        {
344            return super.explainAllProfileUsesOnWorkspacesByGroups(profileId, workspacesContexts);
345        }
346        return Map.of();
347    }
348    
349    @Override
350    public Map<ExplanationObject, Map<UserIdentity, AccessExplanation>> explainAllProfileUsesOnWorkspacesByUser(String profileId, Set<Object> workspacesContexts)
351    {
352        Profile profile = _rightProfileDAO.getProfile(profileId);
353        if (profile != null
354            && _rightProfileDAO.getRights(profile).contains(THESAURUS_RIGHTS_EDIT_TERM))
355        {
356            return super.explainAllProfileUsesOnWorkspacesByUser(profileId, workspacesContexts);
357        }
358        return Map.of();
359    }
360}