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