001/*
002 *  Copyright 2016 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.workspaces.project.rights;
017
018import java.util.Arrays;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Objects;
023import java.util.Set;
024import java.util.stream.Collectors;
025import java.util.stream.Stream;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.commons.lang3.StringUtils;
032import org.apache.http.annotation.Obsolete;
033
034import org.ametys.core.right.Profile;
035import org.ametys.core.right.RightManager;
036import org.ametys.core.right.RightManager.RightResult;
037import org.ametys.core.right.RightProfilesDAO;
038import org.ametys.core.ui.Callable;
039import org.ametys.core.user.CurrentUserProvider;
040import org.ametys.core.user.UserIdentity;
041import org.ametys.plugins.explorer.ExplorerNode;
042import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
043import org.ametys.plugins.repository.AmetysObject;
044import org.ametys.plugins.repository.AmetysObjectResolver;
045import org.ametys.plugins.workspaces.WorkspacesConstants;
046import org.ametys.plugins.workspaces.WorkspacesHelper;
047import org.ametys.plugins.workspaces.about.AboutWorkspaceModule;
048import org.ametys.plugins.workspaces.alert.AlertWorkspaceModule;
049import org.ametys.plugins.workspaces.calendars.CalendarWorkspaceModule;
050import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule;
051import org.ametys.plugins.workspaces.members.MembersWorkspaceModule;
052import org.ametys.plugins.workspaces.minisite.MiniSiteWorkspaceModule;
053import org.ametys.plugins.workspaces.news.NewsWorkspaceModule;
054import org.ametys.plugins.workspaces.project.ProjectConstants;
055import org.ametys.plugins.workspaces.project.ProjectManager;
056import org.ametys.plugins.workspaces.project.modules.WorkspaceModule;
057import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint;
058import org.ametys.plugins.workspaces.project.objects.Project;
059import org.ametys.plugins.workspaces.wall.WallContentModule;
060import org.ametys.runtime.config.Config;
061import org.ametys.runtime.plugin.component.AbstractLogEnabled;
062
063/**
064 * Helper related to rights management for projects.
065 */
066public class ProjectRightHelper extends AbstractLogEnabled implements Serviceable, Component
067{
068    /** Avalon Role */
069    public static final String ROLE = ProjectRightHelper.class.getName();
070    
071    @Obsolete // For v1 project only
072    private static final String __PROJECT_RIGHT_PROFILE = "PROJECT";
073    
074    /** Ametys object resolver */
075    protected AmetysObjectResolver _resolver;
076    
077    /** Project manager */
078    protected ProjectManager _projectManager;
079    
080    /** Right manager */
081    protected RightManager _rightManager;
082    
083    /** Right profiles manager */
084    protected RightProfilesDAO _rightProfilesDao;
085    
086    /** Current user provider */
087    protected CurrentUserProvider _currentUserProvider;
088    
089    /** Workspace Module ExtensionPoint */
090    protected WorkspaceModuleExtensionPoint _workspaceModuleEP;
091    
092    /** Association ContentTypeId, Module */
093    protected Map<String, WorkspaceModule> _contentTypesToModule;
094
095    /** Module managers EP */
096    protected WorkspaceModuleExtensionPoint _moduleManagerEP;
097
098    /** Workspace helper */
099    protected WorkspacesHelper _workspaceHelper;
100    
101    private Set<String> _profileIds;
102    
103    @Override
104    public void service(ServiceManager manager) throws ServiceException
105    {
106        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
107        _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE);
108        _rightManager = (RightManager) manager.lookup(RightManager.ROLE);
109        _rightProfilesDao = (RightProfilesDAO) manager.lookup(RightProfilesDAO.ROLE);
110        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
111        _moduleManagerEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE);
112        _workspaceHelper = (WorkspacesHelper) manager.lookup(WorkspacesHelper.ROLE);
113        
114        _workspaceModuleEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE);
115        _contentTypesToModule = Map.of(
116                WorkspacesConstants.WALL_CONTENT_CONTENT_TYPE_ID, _workspaceModuleEP.getModule(WallContentModule.WALLCONTENT_MODULE_ID),
117                WorkspacesConstants.PROJECT_NEWS_CONTENT_TYPE_ID, _workspaceModuleEP.getModule(NewsWorkspaceModule.NEWS_MODULE_ID),
118                WorkspacesConstants.PROJECT_ALERT_CONTENT_TYPE_ID, _workspaceModuleEP.getModule(AlertWorkspaceModule.ALERT_MODULE_ID),
119                WorkspacesConstants.PROJECT_ARTICLE_CONTENT_TYPE, _workspaceModuleEP.getModule(MiniSiteWorkspaceModule.MINISITE_MODULE_ID),
120                WorkspacesConstants.ABOUT_CONTENT_TYPE, _workspaceModuleEP.getModule(AboutWorkspaceModule.ABOUT_MODULE_ID)
121        );
122    }
123    
124    /**
125     * The association of all project content types and associated modules
126     * @return The association
127     */
128    public Map<String, WorkspaceModule> getProjectContentTypesAndModules()
129    {
130        return _contentTypesToModule;
131    }
132    
133    /**
134     * Retrieves all project profiles ids given the "profile list" configuration parameter
135     * Profile order is guaranteed to be the same as in the configuration parameter.
136     * @return the projects
137     */
138    public synchronized Set<String> getProfilesIds()
139    {
140        if (_profileIds == null)
141        {
142            String rawProjectProfileIds = StringUtils.defaultString(Config.getInstance().getValue("workspaces.profile.list"));
143            _profileIds = Arrays.stream(StringUtils.split(rawProjectProfileIds, ',')).collect(Collectors.toSet());
144        }
145        return _profileIds;
146    }
147    
148    /**
149     * Retrieves all project profile given the "profile list" configuration parameter
150     * Profile order is guaranteed to be the same as in the configuration parameter.
151     * @return the projects
152     */
153    public Set<Profile> getProfiles()
154    {
155        // getProfiles(null) to get only shared profile
156        Map<String, Profile> profileMap = _rightProfilesDao.getProfiles(null).stream().collect(Collectors.toMap(Profile::getId, item -> item));
157        
158        // Collect project profiles (unexisting entries are filtered out).
159        return getProfilesIds().stream()
160            .map(id ->
161            {
162                Profile p = profileMap.get(id);
163                
164                // log null entries
165                if (p == null)
166                {
167                    getLogger().warn("Could not find profile with id '{}'.", id);
168                }
169                
170                return p;
171            })
172            .filter(Objects::nonNull)
173            .collect(Collectors.toSet());
174    }
175    
176    /**
177     * Get the list of profiles and the list of modules available for rights affectation in the project.
178     * @param projectName The project to check if the modules are activated. Can be null to ignore
179     * @return the project rights data
180     */
181    @Callable
182    public Map<String, Object> getProjectRightsData(String projectName)
183    {
184        // profiles
185        List<Object> profiles = getProfiles()
186                .stream()
187                .map(this::_getProfileRightData)
188                .collect(Collectors.toList());
189
190        Project project = projectName != null ? _projectManager.getProject(projectName) : null;
191
192        // modules
193        Stream<Map<String, Object>> stream = _moduleManagerEP.getExtensionsIds().stream().map(moduleId -> _moduleManagerEP.getExtension(moduleId)).map(module -> _getModuleRightData(project, module));
194        List<Object> modules = stream.filter(Objects::nonNull).collect(Collectors.toList());
195        
196        Map<String, Object> result = new HashMap<>();
197        result.put("profiles", profiles);
198        result.put("modules", modules);
199        
200        return result;
201    }
202    
203    private Map<String, Object> _getProfileRightData(Profile profile)
204    {
205        Map<String, Object> data = new HashMap<>();
206        data.put("id", profile.getId());
207        data.put("label", profile.getLabel());
208        return data;
209    }
210    
211    private Map<String, Object> _getModuleRightData(Project project, WorkspaceModule module)
212    {
213        if (project != null && !_projectManager.isModuleActivated(project, module.getId()))
214        {
215            return null;
216        }
217        
218        Map<String, Object> data = new HashMap<>();
219        data.put("id", module.getId());
220        data.put("label", module.getModuleTitle());
221        return data;
222    }
223    
224    /**
225     * Determines if the current user can view the members of a project
226     * @param project the project
227     * @return true if user can view members
228     */
229    public boolean canViewMembers(Project project)
230    {
231        return _rightManager.currentUserHasReadAccess(project);
232    }
233    
234    /**
235     * Determines if the current user has right to add member on project
236     * @param project the project
237     * @return true if user can add member
238     */
239    public boolean canAddMember(Project project)
240    {
241        MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID);
242        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
243        {
244            AmetysObject moduleRoot = module.getModuleRoot(project, false);
245            return moduleRoot != null && _rightManager.currentUserHasRight(ProjectConstants.RIGHT_PROJECT_ADD_MEMBER, moduleRoot) == RightResult.RIGHT_ALLOW;
246        }
247        
248        return false;
249    }
250    
251    /**
252     * Determines if the current user has right to edit member on project
253     * @param project the project
254     * @return true if user can edit member
255     */
256    public boolean canEditMember(Project project)
257    {
258        return canAddMember(project);
259    }
260    
261    /**
262     * Determines if the current user has right to add member on project
263     * @param project the project
264     * @return true if user can remove member
265     */
266    public boolean canRemoveMember(Project project)
267    {
268        return _hasRightOnMembers(project, ProjectConstants.RIGHT_PROJECT_REMOVE_MEMBER);
269    }
270    
271    private boolean _hasRightOnMembers(Project project, String rightId)
272    {
273        MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID);
274        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
275        {
276            AmetysObject moduleRoot = module.getModuleRoot(project, false);
277            return moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW;
278        }
279        
280        return false;
281    }
282    
283    /**
284     * Determines if the current user has right to add tags on project
285     * @param project the project
286     * @return true if user can add tags
287     */
288    public boolean canAddTag(Project project)
289    {
290        return _hasRightOnTagsOrPlaces(project, ProjectConstants.RIGHT_PROJECT_ADD_TAG);
291    }
292    
293    /**
294     * Determines if the current user has right to remove tags on project
295     * @param project the project
296     * @return true if user can remove tags
297     */
298    public boolean canRemoveTag(Project project)
299    {
300        return _hasRightOnTagsOrPlaces(project, ProjectConstants.RIGHT_PROJECT_DELETE_TAG);
301    }
302    
303    private boolean _hasRightOnTagsOrPlaces(Project project, String rightId)
304    {
305        WorkspaceModule module = _moduleManagerEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID);
306        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
307        {
308            AmetysObject moduleRoot = module.getModuleRoot(project, false);
309            if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW)
310            {
311                return true;
312            }
313        }
314        
315        module = _moduleManagerEP.getModule(DocumentWorkspaceModule.DOCUMENT_MODULE_ID);
316        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
317        {
318            AmetysObject moduleRoot = module.getModuleRoot(project, false);
319            if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW)
320            {
321                return true;
322            }
323        }
324        
325        return false;
326    }
327    
328    /**
329     * Determines if current user has read access to module on current project
330     * @param moduleId the module id
331     * @return true if current user has read access, false otherwise
332     */
333    public boolean hasReadAccessOnModule(String moduleId)
334    {
335        return hasReadAccessOnModule(_workspaceHelper.getProjectFromRequest(), moduleId);
336    }
337    
338    /**
339     * Determines if current user has read access to module on given project
340     * @param project the project
341     * @param moduleId the module id
342     * @return true if current user has right, false otherwise
343     */
344    public boolean hasReadAccessOnModule(Project project, String moduleId)
345    {
346        return hasReadAccessOnModule(project, moduleId, _currentUserProvider.getUser());
347    }
348    
349    /**
350     * Determines if current user has read access to module on given project
351     * @param project the project
352     * @param moduleId the module id
353     * @param userIdentity the user identity
354     * @return true if current user has right, false otherwise
355     */
356    public boolean hasReadAccessOnModule(Project project, String moduleId, UserIdentity userIdentity)
357    {
358        WorkspaceModule module = _workspaceModuleEP.getModule(moduleId);
359        if (module != null && project != null)
360        {
361            ModifiableResourceCollection moduleRoot = module.getModuleRoot(project, false);
362            if (moduleRoot != null)
363            {
364                return  _rightManager.hasReadAccess(userIdentity, moduleRoot);
365            }
366        }
367        return false;
368    }
369    
370    /**
371     * Determines if current user has a given right on module on current project
372     * @param rightId the right to check
373     * @param moduleId the module id
374     * @return true if current user has read access, false otherwise
375     */
376    public boolean hasRightOnModule(String rightId, String moduleId)
377    {
378        return hasRightOnModule(_workspaceHelper.getProjectFromRequest(), rightId, moduleId);
379    }
380    
381    /**
382     * Determines if current user has a given right on module on given project
383     * @param project the project
384     * @param rightId the right to check
385     * @param moduleId the module id
386     * @return true if current user has right, false otherwise
387     */
388    public boolean hasRightOnModule(Project project, String rightId, String moduleId)
389    {
390        WorkspaceModule module = _workspaceModuleEP.getModule(moduleId);
391        if (module != null && project != null)
392        {
393            ModifiableResourceCollection moduleRoot = module.getModuleRoot(project, false);
394            if (moduleRoot != null)
395            {
396                return  _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW;
397            }
398        }
399    
400        return false;
401    }
402    
403    /**
404     * Test if the current user has the right on the project
405     * @param rightId The right id
406     * @param project The project
407     * @return true if has right
408     */
409    public boolean hasRight(String rightId, Project project)
410    {
411        return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, project) == RightResult.RIGHT_ALLOW;
412    }
413    
414    /**
415     * Test if the current user has a read access on current project
416     * @return true if has read access
417     */
418    public boolean hasReadAccess()
419    {
420        return hasReadAccess(_workspaceHelper.getProjectFromRequest());
421    }
422    
423    /**
424     * Test if the current user has a read access on the project
425     * @param project The project
426     * @return true if has read access
427     */
428    public boolean hasReadAccess(Project project)
429    {
430        return project != null && _rightManager.hasReadAccess(_currentUserProvider.getUser(), project);
431    }
432    
433    /**
434     * Test if the current user has the right on an explorer node
435     * @param rightId The right id
436     * @param explorerNode The explorer node
437     * @return true if has right
438     */
439    public boolean hasRight(String rightId, ExplorerNode explorerNode)
440    {
441        return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, explorerNode) == RightResult.RIGHT_ALLOW;
442    }
443}