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.ProfileAssignmentStorageExtensionPoint; 036import org.ametys.core.right.RightManager; 037import org.ametys.core.right.RightManager.RightResult; 038import org.ametys.core.right.RightProfilesDAO; 039import org.ametys.core.ui.Callable; 040import org.ametys.core.user.CurrentUserProvider; 041import org.ametys.plugins.explorer.ExplorerNode; 042import org.ametys.plugins.explorer.resources.Resource; 043import org.ametys.plugins.repository.AmetysObject; 044import org.ametys.plugins.repository.AmetysObjectResolver; 045import org.ametys.plugins.repository.UnknownAmetysObjectException; 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.members.JCRProjectMember; 050import org.ametys.plugins.workspaces.members.JCRProjectMember.MemberType; 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.runtime.config.Config; 056import org.ametys.runtime.i18n.I18nizableText; 057import org.ametys.runtime.plugin.component.AbstractLogEnabled; 058import org.ametys.runtime.plugin.component.PluginAware; 059 060/** 061 * Helper related to rights management for projects. 062 */ 063public class ProjectRightHelper extends AbstractLogEnabled implements Serviceable, Component, PluginAware 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 /** Project Right to add a member to a project */ 072 private static final String __RIGHT_ADD_MEMBER = "Plugins_Workspaces_Rights_Service_Module_Members_Add"; 073 074 /** Project Right to remove a member from the project */ 075 private static final String __RIGHT_REMOVE_MEMBER = "Plugins_Workspaces_Rights_Service_Module_Members_Remove"; 076 077 /** Right to add a tag */ 078 private static final String __RIGHT_ADD_TAG = "Plugins_Workspaces_Rights_Project_Add_Tag"; 079 /** Right to delete a tag */ 080 private static final String __RIGHT_DELETE_TAG = "Plugins_Workspaces_Rights_Project_Delete_Tag"; 081 /** Right to add a place */ 082 private static final String __RIGHT_ADD_PLACE = "Plugins_Workspaces_Rights_Project_Add_Place"; 083 /** Right to delete a place */ 084 private static final String __RIGHT_DELETE_PLACE = "Plugins_Workspaces_Rights_Project_Delete_Place"; 085 086 /** Ametys object resolver */ 087 protected AmetysObjectResolver _resolver; 088 089 /** Project manager */ 090 protected ProjectManager _projectManager; 091 092 /** Right manager */ 093 protected RightManager _rightManager; 094 095 /** Right profiles manager */ 096 protected RightProfilesDAO _rightProfilesDao; 097 098 /** Current user provider */ 099 protected CurrentUserProvider _currentUserProvider; 100 101 /** Module managers EP */ 102 protected WorkspaceModuleExtensionPoint _moduleManagerEP; 103 104 /** The profile storage */ 105 private ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP; 106 107 private String _pluginName; 108 109 110 @Override 111 public void service(ServiceManager manager) throws ServiceException 112 { 113 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 114 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 115 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 116 _rightProfilesDao = (RightProfilesDAO) manager.lookup(RightProfilesDAO.ROLE); 117 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 118 _moduleManagerEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 119 _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) manager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE); 120 } 121 122 public void setPluginInfo(String pluginName, String featureName, String id) 123 { 124 _pluginName = pluginName; 125 } 126 /** 127 * Retrieves all project profile given the "profile list" configuration parameter 128 * Profile order is guaranteed to be the same as in the configuration parameter. 129 * @return the projects 130 */ 131 public List<Profile> getProfileList() 132 { 133 Map<String, Profile> profileMap = _rightProfilesDao.getProfiles().stream().collect(Collectors.toMap(Profile::getId, item -> item)); 134 String rawProjectProfileIds = StringUtils.defaultString(Config.getInstance().getValue("workspaces.profile.list")); 135 136 // Collect project profiles (unexisting entries are filtered out). 137 return Arrays.stream(StringUtils.split(rawProjectProfileIds, ',')) 138 .map(id -> 139 { 140 Profile p = profileMap.get(id); 141 142 // log null entries 143 if (p == null) 144 { 145 getLogger().warn("Could not find profile with id '{}'.", id); 146 } 147 return p; 148 }) 149 .filter(Objects::nonNull) 150 .collect(Collectors.toList()); 151 } 152 153 /** 154 * Get the first allowed profile on project's module for a given member 155 * @param project the project 156 * @param module The workspace module 157 * @param member the member 158 * @return the first allowed profile or null if not found 159 */ 160 public String getAllowedProfileOnModule (Project project, WorkspaceModule module, JCRProjectMember member) 161 { 162 List<String> profileIds = getProfileList() 163 .stream() 164 .map(p -> p.getId()) 165 .collect(Collectors.toList()); 166 167 AmetysObject moduleObject = module.getModuleRoot(project, false); 168 Set<String> allowedProfilesForUser = MemberType.GROUP.toString().equals(member.getType()) 169 ? _profileAssignmentStorageEP.getAllowedProfilesForGroup(moduleObject, member.getGroup()) 170 : _profileAssignmentStorageEP.getAllowedProfilesForUser(moduleObject, member.getUser()); 171 172 for (String allowedProfile : allowedProfilesForUser) 173 { 174 if (profileIds.contains(allowedProfile)) 175 { 176 // Get the first allowed profile among the project's members profiles 177 return allowedProfile; 178 } 179 } 180 181 return null; 182 } 183 184 /** 185 * Get the list of profiles and the list of modules available for rights affectation in the project. 186 * @param projectName The project to check if the modules are activated. Can be null to ignore 187 * @return the project rights data 188 */ 189 @Callable 190 public Map<String, Object> getProjectRightsData(String projectName) 191 { 192 return getProjectRightsData(projectName, false); 193 } 194 195 /** 196 * Get the list of profiles and the list of modules available for rights affectation in the project. 197 * @param projectName The project to check if the modules are activated. Can be null to ignore 198 * @param includeAllModules True to include modules not manager by the WorkspaceModuleManagerExtensionPoint 199 * @return the project rights data 200 */ 201 @Callable 202 @Obsolete // For v1 projects only 203 public Map<String, Object> getProjectRightsData(String projectName, boolean includeAllModules) 204 { 205 // profiles 206 List<Object> profiles = getProfileList() 207 .stream() 208 .map(this::_getProfileRightData) 209 .collect(Collectors.toList()); 210 211 212 Project project = projectName != null ? _projectManager.getProject(projectName) : null; 213 214 // modules 215 Stream<Map<String, Object>> stream = _moduleManagerEP.getExtensionsIds().stream().map(moduleId -> _moduleManagerEP.getExtension(moduleId)).map(module -> _getModuleRightData(project, module)); 216 List<Object> modules = stream.filter(Objects::nonNull).collect(Collectors.toList()); 217 218 Map<String, Object> result = new HashMap<>(); 219 result.put("profiles", profiles); 220 result.put("modules", modules); 221 222 return result; 223 } 224 225 private Map<String, Object> _getProfileRightData(Profile profile) 226 { 227 Map<String, Object> data = new HashMap<>(); 228 data.put("id", profile.getId()); 229 data.put("label", profile.getLabel()); 230 return data; 231 } 232 233 private Map<String, Object> _getModuleRightData(Project project, WorkspaceModule module) 234 { 235 if (project != null && !_projectManager.isModuleActivated(project, module.getId())) 236 { 237 return null; 238 } 239 240 Map<String, Object> data = new HashMap<>(); 241 data.put("id", module.getId()); 242 data.put("label", module.getModuleTitle()); 243 return data; 244 } 245 246 // For v1 projects only 247 @Obsolete 248 private Map<String, Object> _getProjectRightData() 249 { 250 Map<String, Object> data = new HashMap<>(); 251 data.put("id", __PROJECT_RIGHT_PROFILE); 252 data.put("label", new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_PROFILE_TO_PROJECT_LABEL")); 253 return data; 254 } 255 256 /** 257 * Determines if the current user can view the members of a project 258 * @param project the project 259 * @return true if user can view members 260 */ 261 public boolean canViewMembers(Project project) 262 { 263 return _rightManager.currentUserHasReadAccess(project); 264 } 265 266 /** 267 * Determines if the current user has right to add member on project 268 * @param project the project 269 * @return true if user can add member 270 */ 271 public boolean canAddMember(Project project) 272 { 273 MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID); 274 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 275 { 276 AmetysObject moduleRoot = module.getModuleRoot(project, false); 277 return moduleRoot != null && _rightManager.currentUserHasRight(__RIGHT_ADD_MEMBER, moduleRoot) == RightResult.RIGHT_ALLOW; 278 } 279 280 return false; 281 } 282 283 /** 284 * Determines if the current user has right to edit member on project 285 * @param project the project 286 * @return true if user can edit member 287 */ 288 public boolean canEditMember(Project project) 289 { 290 return canAddMember(project); 291 } 292 293 /** 294 * Determines if the current user has right to add member on project 295 * @param project the project 296 * @return true if user can remove member 297 */ 298 public boolean canRemoveMember(Project project) 299 { 300 return _hasRightOnMembers(project, __RIGHT_REMOVE_MEMBER); 301 } 302 303 private boolean _hasRightOnMembers(Project project, String rightId) 304 { 305 MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID); 306 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 307 { 308 AmetysObject moduleRoot = module.getModuleRoot(project, false); 309 return moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW; 310 } 311 312 return false; 313 } 314 315 /** 316 * Determines if the current user has right to add tags on project 317 * @param project the project 318 * @return true if user can add tags 319 */ 320 public boolean canAddTag(Project project) 321 { 322 return _hasRightOnTagsOrPlaces(project, __RIGHT_ADD_TAG); 323 } 324 325 /** 326 * Determines if the current user has right to remove tags on project 327 * @param project the project 328 * @return true if user can remove tags 329 */ 330 public boolean canRemoveTag(Project project) 331 { 332 return _hasRightOnTagsOrPlaces(project, __RIGHT_DELETE_TAG); 333 } 334 335 /** 336 * Determines if the current user has right to add places on project 337 * @param project the project 338 * @return true if user can add places 339 */ 340 public boolean canAddPlace(Project project) 341 { 342 return _hasRightOnTagsOrPlaces(project, __RIGHT_ADD_PLACE); 343 } 344 345 /** 346 * Determines if the current user has right to remove places on project 347 * @param project the project 348 * @return true if user can remove places 349 */ 350 public boolean canRemovePlace(Project project) 351 { 352 return _hasRightOnTagsOrPlaces(project, __RIGHT_DELETE_PLACE); 353 } 354 355 private boolean _hasRightOnTagsOrPlaces(Project project, String rightId) 356 { 357 WorkspaceModule module = _moduleManagerEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID); 358 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 359 { 360 AmetysObject moduleRoot = module.getModuleRoot(project, false); 361 if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW) 362 { 363 return true; 364 } 365 } 366 367 module = _moduleManagerEP.getModule(DocumentWorkspaceModule.DODUMENT_MODULE_ID); 368 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 369 { 370 AmetysObject moduleRoot = module.getModuleRoot(project, false); 371 if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW) 372 { 373 return true; 374 } 375 } 376 377 return false; 378 } 379 380 /** 381 * Test if the current user has the right on the project 382 * @param rightId The right id 383 * @param projectId The project id 384 * @return true if has right 385 */ 386 @Callable 387 public boolean hasRightOnProject(String rightId, String projectId) 388 { 389 try 390 { 391 return hasRight(rightId, _resolver.<Project>resolveById(projectId)); 392 } 393 catch (UnknownAmetysObjectException e) 394 { 395 getLogger().warn("Trying to test right on an unknown project. Requested id : {}", projectId, e); 396 return false; 397 } 398 } 399 400 /** 401 * Test if the current user has the right on the project 402 * @param rightId The right id 403 * @param project The project 404 * @return true if has right 405 */ 406 public boolean hasRight(String rightId, Project project) 407 { 408 return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, project) == RightResult.RIGHT_ALLOW; 409 } 410 411 /** 412 * Test if the current user has a read access on the project 413 * @param projectId The project id 414 * @return true if has read access 415 */ 416 @Callable 417 public boolean hasReadAccessOnProject(String projectId) 418 { 419 try 420 { 421 return hasReadAccess(_resolver.<Project>resolveById(projectId)); 422 } 423 catch (UnknownAmetysObjectException e) 424 { 425 getLogger().warn("Trying to test read access on an unknown project. Requested id : {}", projectId, e); 426 return false; 427 } 428 } 429 430 /** 431 * Test if the current user has a read access on the project 432 * @param project The project 433 * @return true if has read access 434 */ 435 public boolean hasReadAccess(Project project) 436 { 437 return _rightManager.hasReadAccess(_currentUserProvider.getUser(), project); 438 } 439 440 /** 441 * Test if the current user has the right on an explorer node 442 * @param rightId The right id 443 * @param explorerNodeId The explorer node id 444 * @return true if has right 445 */ 446 @Callable 447 public boolean hasRightOnExplorerNode(String rightId, String explorerNodeId) 448 { 449 try 450 { 451 return hasRight(rightId, _resolver.<ExplorerNode>resolveById(explorerNodeId)); 452 } 453 catch (UnknownAmetysObjectException e) 454 { 455 getLogger().warn("Trying to test right on an unknown explorer node. Requested id : {}", explorerNodeId, e); 456 return false; 457 } 458 } 459 460 /** 461 * Test if the current user has the right on an explorer node 462 * @param rightId The right id 463 * @param explorerNode The explorer node 464 * @return true if has right 465 */ 466 public boolean hasRight(String rightId, ExplorerNode explorerNode) 467 { 468 return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, explorerNode) == RightResult.RIGHT_ALLOW; 469 } 470 471 /** 472 * Test if the current user has a read access on an explorer node 473 * @param explorerNodeId The explorer node id 474 * @return true if has read access 475 */ 476 @Callable 477 public boolean hasReadAccessOnExplorerNode(String explorerNodeId) 478 { 479 try 480 { 481 return hasReadAccess(_resolver.<ExplorerNode>resolveById(explorerNodeId)); 482 } 483 catch (UnknownAmetysObjectException e) 484 { 485 getLogger().warn("Trying to test read access on an unknown explorer node. Requested id : {}", explorerNodeId, e); 486 return false; 487 } 488 } 489 490 /** 491 * Test if the current user has a read access on an explorer node 492 * @param explorerNode The explorer node 493 * @return true if has read access 494 */ 495 public boolean hasReadAccess(ExplorerNode explorerNode) 496 { 497 return _rightManager.currentUserHasReadAccess(explorerNode); 498 } 499 500 /** 501 * Test if the current user has a read access on a resource 502 * @param resource The resource 503 * @return true if has read access 504 */ 505 public boolean hasReadAccess(Resource resource) 506 { 507 return _rightManager.currentUserHasReadAccess(resource); 508 } 509}