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.workspaces.editionfo;
017
018import java.util.Collections;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.avalon.framework.service.Serviceable;
027import org.slf4j.Logger;
028
029import org.ametys.core.group.GroupIdentity;
030import org.ametys.core.right.AccessController;
031import org.ametys.core.right.AccessControllerExtensionPoint;
032import org.ametys.core.user.UserIdentity;
033import org.ametys.plugins.explorer.resources.ResourceCollection;
034import org.ametys.plugins.explorer.rights.ResourceAccessController;
035import org.ametys.plugins.frontedition.AmetysFrontEditionHelper;
036import org.ametys.plugins.workspaces.project.ProjectManager;
037import org.ametys.plugins.workspaces.project.modules.WorkspaceModule;
038import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint;
039import org.ametys.plugins.workspaces.project.objects.Project;
040import org.ametys.runtime.plugin.component.LogEnabled;
041import org.ametys.web.repository.page.Page;
042import org.ametys.web.repository.page.PagesContainer;
043
044/**
045 * This controller will grant the right "Front_Edition_Access_Right" on the "/cms/{site}" context to any user that have the Edition fo rights on the project corresponding to this site
046 * Beware that this controller works with ResourceAccessController... that should be enabled (and if another AccessController can give rights to resources... that's too bad)
047 */
048public class EditionFOAccessController implements AccessController, Serviceable, LogEnabled
049{
050    private ProjectManager _projectManager;
051    private WorkspaceModuleExtensionPoint _workspaceModuleExtensionPoint;
052    private Logger _logger;
053    private AccessControllerExtensionPoint _accessControllerExtensionPoint;
054    private AccessController _accessController;
055
056    public void service(ServiceManager manager) throws ServiceException
057    {
058        _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE);
059        _workspaceModuleExtensionPoint = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE);
060        _accessControllerExtensionPoint = (AccessControllerExtensionPoint) manager.lookup(AccessControllerExtensionPoint.ROLE);
061    }
062    
063    public void setLogger(Logger logger)
064    {
065        _logger = logger;
066    }
067    
068    private AccessController _getAccessController()
069    {
070        if (_accessController == null)
071        {
072            _accessController = _accessControllerExtensionPoint.getExtension(ResourceAccessController.class.getName());
073        }
074        return _accessController;
075    }
076    
077    private ResourceCollection _getEditionFOModuleRootFromContext(Object object)
078    {
079        String siteName = _getSiteFromContext(object);
080        for (String projectName : _projectManager.getProjectsForSite(siteName))
081        {
082            Project project = _projectManager.getProject(projectName);
083            if (project == null)
084            {
085                _logger.warn("Cannot find project '{}' associated to the site '{}'.", projectName, siteName);
086                continue;
087            }
088            
089            WorkspaceModule editionFOWorkspaceModule = _workspaceModuleExtensionPoint.getExtension(EditionFOWorkspaceModule.EDITIONFO_MODULE_ID);
090            
091            if (!_isPageInProjectWiki(editionFOWorkspaceModule, project, object))
092            {
093                return null;
094            }
095            
096            return editionFOWorkspaceModule.getModuleRoot(project, false);
097        }
098        
099        return null;
100    }
101    
102    private String _getSiteFromContext(Object context)
103    {
104        if (context instanceof PagesContainer)
105        {
106            return ((PagesContainer) context).getSiteName();
107        }
108        return null;
109    }
110    
111    private boolean _isPageInProjectWiki(WorkspaceModule editionFOWorkspaceModule, Project project, Object object)
112    {
113        if (!(object instanceof PagesContainer))
114        {
115            return false;
116        }
117        PagesContainer page = (PagesContainer) object;
118        String pagePath = page.getPath();
119        
120        return editionFOWorkspaceModule.getModulePages(project, page.getSitemapName())
121                                       .stream()
122                                       .anyMatch(wikiPage -> pagePath.equals(wikiPage.getPath()) || pagePath.contains(wikiPage.getPath() + "/"));
123    }
124    
125    public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object)
126    {
127        if (AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID.equals(rightId) || object instanceof Page)
128        {
129            Set<AccessResult> results = new HashSet<>();
130            
131            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
132            if (moduleRoot != null)
133            {
134                AccessResult permission = _getAccessController().getPermission(user, userGroups, rightId, moduleRoot);
135                results.add(permission);
136            }
137            
138            return AccessResult.merge(results);
139        }
140        else
141        {
142            return AccessResult.UNKNOWN;
143        }
144    }
145
146    public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
147    {
148        if (object instanceof Page)
149        {
150            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
151            if (moduleRoot != null)
152            {
153                return _getAccessController().getReadAccessPermission(user, userGroups, moduleRoot);
154            }
155        }
156        
157        return AccessResult.UNKNOWN;
158    }
159
160    public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
161    {
162        AccessResult accessResult = getPermission(user, userGroups, AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID, object);
163        if (accessResult == AccessResult.UNKNOWN)
164        {
165            return Collections.EMPTY_MAP;
166        }
167        else
168        {
169            Map<String, AccessResult> permissions = new HashMap<>();
170            permissions.put(AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID, accessResult);
171            return permissions;
172        }
173    }
174
175    public AccessResult getPermissionForAnonymous(String rightId, Object object)
176    {
177        if (AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID.equals(rightId) || object instanceof Page)
178        {
179            Set<AccessResult> results = new HashSet<>();
180            
181            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
182            if (moduleRoot != null)
183            {
184                AccessResult permission = _getAccessController().getPermissionForAnonymous(rightId, moduleRoot);
185                results.add(permission);
186            }
187            
188            return AccessResult.merge(results);
189        }
190        else
191        {
192            return AccessResult.UNKNOWN;
193        }
194    }
195
196    public AccessResult getReadAccessPermissionForAnonymous(Object object)
197    {
198        if (object instanceof Page)
199        {
200            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
201            if (moduleRoot != null)
202            {
203                return _getAccessController().getReadAccessPermissionForAnonymous(moduleRoot);
204            }
205        }
206        
207        return AccessResult.UNKNOWN;
208    }
209
210    public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object)
211    {
212        if (AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID.equals(rightId) || object instanceof Page)
213        {
214            Set<AccessResult> results = new HashSet<>();
215            
216            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
217            if (moduleRoot != null)
218            {
219                AccessResult permission = _getAccessController().getPermissionForAnyConnectedUser(rightId, moduleRoot);
220                results.add(permission);
221            }
222            
223            return AccessResult.merge(results);
224        }
225        else
226        {
227            return AccessResult.UNKNOWN;
228        }
229    }
230
231    public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object)
232    {
233        if (object instanceof Page)
234        {
235            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
236            if (moduleRoot != null)
237            {
238                return _getAccessController().getReadAccessPermissionForAnyConnectedUser(moduleRoot);
239            }
240        }
241        
242        return AccessResult.UNKNOWN;
243    }
244
245    public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object)
246    {
247        if (AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID.equals(rightId) || object instanceof Page)
248        {
249            Map<UserIdentity, AccessResult> permissionsByUser = new HashMap<>();
250            
251            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
252            if (moduleRoot != null)
253            {
254                Map<UserIdentity, AccessResult> permissionByUser = _getAccessController().getPermissionByUser(rightId, moduleRoot);
255                for (UserIdentity userIdentity : permissionByUser.keySet())
256                {
257                    if (permissionsByUser.containsKey(userIdentity))
258                    {
259                        permissionsByUser.put(userIdentity, AccessResult.merge(permissionsByUser.get(userIdentity), permissionByUser.get(userIdentity)));
260                    }
261                    else
262                    {
263                        permissionsByUser.put(userIdentity, permissionByUser.get(userIdentity));
264                    }
265                }
266            }
267
268            return permissionsByUser;
269        }
270        else
271        {
272            return Collections.EMPTY_MAP;
273        }
274    }
275
276    public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object)
277    {
278        if (object instanceof Page)
279        {
280            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
281            if (moduleRoot != null)
282            {
283                return _getAccessController().getReadAccessPermissionByUser(moduleRoot);
284            }
285        }
286        
287        return Collections.EMPTY_MAP;
288    }
289
290    public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object)
291    {
292        if (AmetysFrontEditionHelper.FRONT_EDITION_RIGHT_ID.equals(rightId) || object instanceof Page)
293        {
294            Map<GroupIdentity, AccessResult> permissionsByGroup = new HashMap<>();
295            
296            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
297            if (moduleRoot != null)
298            {
299                Map<GroupIdentity, AccessResult> permissionByGroup = _getAccessController().getPermissionByGroup(rightId, moduleRoot);
300                for (GroupIdentity groupIdentity : permissionByGroup.keySet())
301                {
302                    if (permissionsByGroup.containsKey(groupIdentity))
303                    {
304                        permissionsByGroup.put(groupIdentity, AccessResult.merge(permissionsByGroup.get(groupIdentity), permissionByGroup.get(groupIdentity)));
305                    }
306                    else
307                    {
308                        permissionsByGroup.put(groupIdentity, permissionByGroup.get(groupIdentity));
309                    }
310                }
311            }
312
313            return permissionsByGroup;
314        }
315        else
316        {
317            return Collections.EMPTY_MAP;
318        }
319    }
320
321    public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object)
322    {
323        if (object instanceof Page)
324        {
325            ResourceCollection moduleRoot = _getEditionFOModuleRootFromContext(object);
326            if (moduleRoot != null)
327            {
328                return _getAccessController().getReadAccessPermissionByGroup(moduleRoot);
329            }
330        }
331        
332        return Collections.EMPTY_MAP;
333    }
334
335    public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId)
336    {
337        // We do not want that this accesscontroller give access to the backoffice (even if #isSupported would not match in this case)
338        return false;
339    }
340
341    public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups)
342    {
343        return false;
344    }
345
346    public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
347    {
348        // We do not want that this accesscontroller give access to the backoffice (even if #isSupported would not match in this case)
349        return false;
350    }
351
352    public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
353    {
354        return false;
355    }
356
357    public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
358    {
359        // We do not want that this accesscontroller give access to the backoffice (even if #isSupported would not match in this case)
360        return false;
361    }
362
363    public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
364    {
365        return false;
366    }
367
368    public boolean isSupported(Object object)
369    {
370        return object instanceof PagesContainer;
371    }
372
373}