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