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        // getProfiles(null) to get only shared profile
150        Map<String, Profile> profileMap = _rightProfilesDao.getProfiles(null).stream().collect(Collectors.toMap(Profile::getId, item -> item));
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        Project project = projectName != null ? _projectManager.getProject(projectName) : null;
185
186        // modules
187        Stream<Map<String, Object>> stream = _moduleManagerEP.getExtensionsIds().stream().map(moduleId -> _moduleManagerEP.getExtension(moduleId)).map(module -> _getModuleRightData(project, module));
188        List<Object> modules = stream.filter(Objects::nonNull).collect(Collectors.toList());
189        
190        Map<String, Object> result = new HashMap<>();
191        result.put("profiles", profiles);
192        result.put("modules", modules);
193        
194        return result;
195    }
196    
197    private Map<String, Object> _getProfileRightData(Profile profile)
198    {
199        Map<String, Object> data = new HashMap<>();
200        data.put("id", profile.getId());
201        data.put("label", profile.getLabel());
202        return data;
203    }
204    
205    private Map<String, Object> _getModuleRightData(Project project, WorkspaceModule module)
206    {
207        if (project != null && !_projectManager.isModuleActivated(project, module.getId()))
208        {
209            return null;
210        }
211        
212        Map<String, Object> data = new HashMap<>();
213        data.put("id", module.getId());
214        data.put("label", module.getModuleTitle());
215        return data;
216    }    
217    
218    /**
219     * Determines if the current user can view the members of a project
220     * @param project the project
221     * @return true if user can view members
222     */
223    public boolean canViewMembers(Project project)
224    {
225        return _rightManager.currentUserHasReadAccess(project);
226    }
227    
228    /**
229     * Determines if the current user has right to add member on project
230     * @param project the project
231     * @return true if user can add member
232     */
233    public boolean canAddMember(Project project)
234    {
235        MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID);
236        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
237        {
238            AmetysObject moduleRoot = module.getModuleRoot(project, false);
239            return moduleRoot != null && _rightManager.currentUserHasRight(ProjectConstants.RIGHT_PROJECT_ADD_MEMBER, moduleRoot) == RightResult.RIGHT_ALLOW;
240        }
241        
242        return false;
243    }
244    
245    /**
246     * Determines if the current user has right to edit member on project
247     * @param project the project
248     * @return true if user can edit member
249     */
250    public boolean canEditMember(Project project)
251    {
252        return canAddMember(project);
253    }
254    
255    /**
256     * Determines if the current user has right to add member on project
257     * @param project the project
258     * @return true if user can remove member
259     */
260    public boolean canRemoveMember(Project project)
261    {
262        return _hasRightOnMembers(project, ProjectConstants.RIGHT_PROJECT_REMOVE_MEMBER);
263    }
264    
265    private boolean _hasRightOnMembers(Project project, String rightId)
266    {
267        MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID);
268        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
269        {
270            AmetysObject moduleRoot = module.getModuleRoot(project, false);
271            return moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW;
272        }
273        
274        return false;
275    }
276    
277    /**
278     * Determines if the current user has right to add tags on project
279     * @param project the project
280     * @return true if user can add tags
281     */
282    public boolean canAddTag(Project project)
283    {
284        return _hasRightOnTagsOrPlaces(project, ProjectConstants.RIGHT_PROJECT_ADD_TAG);
285    }
286    
287    /**
288     * Determines if the current user has right to remove tags on project
289     * @param project the project
290     * @return true if user can remove tags
291     */
292    public boolean canRemoveTag(Project project)
293    {
294        return _hasRightOnTagsOrPlaces(project, ProjectConstants.RIGHT_PROJECT_DELETE_TAG);
295    }
296    
297    private boolean _hasRightOnTagsOrPlaces(Project project, String rightId)
298    {
299        WorkspaceModule module = _moduleManagerEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID);
300        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
301        {
302            AmetysObject moduleRoot = module.getModuleRoot(project, false);
303            if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW)
304            {
305                return true;
306            }
307        }
308        
309        module = _moduleManagerEP.getModule(DocumentWorkspaceModule.DOCUMENT_MODULE_ID);
310        if (module != null && _projectManager.isModuleActivated(project, module.getId()))
311        {
312            AmetysObject moduleRoot = module.getModuleRoot(project, false);
313            if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW)
314            {
315                return true;
316            }
317        }
318        
319        return false;
320    }
321    
322    /**
323     * Test if the current user has the right on the project
324     * @param rightId The right id
325     * @param project The project
326     * @return true if has right
327     */
328    public boolean hasRight(String rightId, Project project)
329    {
330        return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, project) == RightResult.RIGHT_ALLOW;
331    }
332    
333    /**
334     * Test if the current user has a read access on the project
335     * @param project The project
336     * @return true if has read access
337     */
338    public boolean hasReadAccess(Project project)
339    {
340        return _rightManager.hasReadAccess(_currentUserProvider.getUser(), project);
341    }
342    
343    /**
344     * Test if the current user has the right on an explorer node
345     * @param rightId The right id
346     * @param explorerNode The explorer node
347     * @return true if has right
348     */
349    public boolean hasRight(String rightId, ExplorerNode explorerNode)
350    {
351        return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, explorerNode) == RightResult.RIGHT_ALLOW;
352    }
353}