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