001/*
002 *  Copyright 2020 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.accesscontroller;
017
018import java.util.Map;
019import java.util.Set;
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.avalon.framework.service.Serviceable;
026
027import org.ametys.core.group.GroupIdentity;
028import org.ametys.core.right.AccessController;
029import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint;
030import org.ametys.core.right.RightManager.RightResult;
031import org.ametys.core.user.UserIdentity;
032import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
033import org.ametys.plugins.explorer.resources.ResourceCollection;
034import org.ametys.plugins.repository.AmetysObject;
035import org.ametys.plugins.repository.AmetysObjectResolver;
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.plugins.workspaces.project.rights.ProjectRightHelper;
041
042/**
043 * Give the read right on modules if the user as any other profile on it.
044 * This controller has no cache by itself on the underlying non cached ProfileAssignmentStorageExtensionPoint ; but as it works only on the read right, the top level cache in the  RightManager do the job
045 */
046public class ModuleAccessController implements AccessController, Serviceable
047{
048    private static Pattern __MODULE_ROOT_PATH_PATTERN = Pattern.compile("^(/ametys:plugins/workspaces/projects/(?:[^/]+)/ametys-internal:resources/(?:[^/]+)).*$");
049    
050    /** The extension point for the profile assignment storages */
051    protected ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP;
052    /** the right helper */
053    protected ProjectRightHelper _projectRightHelper;
054    /** The Ametys object resolver */
055    protected AmetysObjectResolver _resolver;
056    /** The module extension point */
057    protected WorkspaceModuleExtensionPoint _moduleEP;
058    /** The project manager */
059    protected ProjectManager _projectManager;
060    
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) manager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE);
064        _projectRightHelper = (ProjectRightHelper) manager.lookup(ProjectRightHelper.ROLE);
065        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
066        _moduleEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE);
067        _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE);
068    }
069    
070    public boolean isSupported(Object object)
071    {
072        if (object instanceof AmetysObject)
073        {
074            String path = ((AmetysObject) object).getPath();
075            return path.startsWith("/ametys:plugins/workspaces/projects/")
076                    && path.contains("/ametys-internal:resources/");
077        }
078        else
079        {
080            return false;
081        }
082    }
083    
084    private boolean _isModuleActivated(ResourceCollection moduleRoot)
085    {
086        Project project = moduleRoot.getParent().getParent();
087        
088        WorkspaceModule module = _moduleEP.getModuleByName(moduleRoot.getName());
089        if (module == null)
090        {
091            throw new IllegalStateException("Can not find module from the module root name '" + moduleRoot.getName() + "'");
092        }
093        
094        return _projectManager.isModuleActivated(project, module.getId());
095    }
096    
097    private ModifiableResourceCollection _getModuleRoot(Object object)
098    {
099        AmetysObject node = (AmetysObject) object;
100        
101        Matcher matcher = __MODULE_ROOT_PATH_PATTERN.matcher(node.getPath());
102        if (matcher.matches())
103        {
104            String rootPath = matcher.group(1);
105            if (node.getPath().equals(rootPath))
106            {
107                return (ModifiableResourceCollection) node;
108            }
109            else
110            {
111                return (ModifiableResourceCollection) _resolver.resolveByPath(rootPath);
112            }
113        }
114        
115        throw new IllegalArgumentException("Node " + node.getPath() + " is not a module path");
116    }
117    
118    public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object)
119    {
120        return AccessResult.UNKNOWN;
121    }
122    
123    public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
124    {
125        ModifiableResourceCollection root = _getModuleRoot(object);
126        
127        if (!_isModuleActivated(root))
128        {
129            return AccessResult.UNKNOWN;
130        }
131        else
132        {
133            return _profileAssignmentStorageEP.getPermissions(user, userGroups, _projectRightHelper.getProfilesIds(), root)
134                    .values().stream()
135                    .anyMatch(ar -> ar.toRightResult() == RightResult.RIGHT_ALLOW) ? AccessResult.USER_ALLOWED : AccessResult.UNKNOWN;
136        }
137    }
138    
139    public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
140    {
141        return Map.of();
142    }
143    
144    public AccessResult getPermissionForAnonymous(String rightId, Object object)
145    {
146        return AccessResult.UNKNOWN;
147    }
148    
149    public AccessResult getReadAccessPermissionForAnonymous(Object object)
150    {
151        ModifiableResourceCollection root = _getModuleRoot(object);
152        
153        if (!_isModuleActivated(root))
154        {
155            return AccessResult.UNKNOWN;
156        }
157        else
158        {
159            return _profileAssignmentStorageEP.getPermissionForAnonymous(_projectRightHelper.getProfilesIds(), root);
160        }
161    }
162    
163    public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object)
164    {
165        return AccessResult.UNKNOWN;
166    }
167    
168    public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object)
169    {
170        ModifiableResourceCollection root = _getModuleRoot(object);
171        
172        if (!_isModuleActivated(root))
173        {
174            return AccessResult.UNKNOWN;
175        }
176        else
177        {
178            return _profileAssignmentStorageEP.getPermissionForAnyConnectedUser(_projectRightHelper.getProfilesIds(), _getModuleRoot(object));
179        }
180    }
181    
182    public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object)
183    {
184        return Map.of();
185    }
186    
187    public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object)
188    {
189        ModifiableResourceCollection root = _getModuleRoot(object);
190        
191        if (!_isModuleActivated(root))
192        {
193            return Map.of();
194        }
195        else
196        {
197            return _profileAssignmentStorageEP.getPermissionsByUser(_projectRightHelper.getProfilesIds(), root);
198        }
199    }
200    
201    public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object)
202    {
203        return Map.of();
204    }
205    
206    public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object)
207    {
208        ModifiableResourceCollection root = _getModuleRoot(object);
209        
210        if (!_isModuleActivated(root))
211        {
212            return Map.of();
213        }
214        else
215        {
216            return _profileAssignmentStorageEP.getPermissionsByGroup(_projectRightHelper.getProfilesIds(), root);
217        }
218    }
219    
220    public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId)
221    {
222        return false;
223    }
224    
225    public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups)
226    {
227        return false; // No need to forward to workspace
228    }
229    
230    public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
231    {
232        return false;
233    }
234    
235    public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
236    {
237        return false; // No need to forward to workspace
238    }
239    
240    public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
241    {
242        return false;
243    }
244    
245    public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
246    {
247        return false; // No need to forward to workspace
248    }
249}