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.core.user.UserIdentity; 041import org.ametys.plugins.explorer.ExplorerNode; 042import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 043import org.ametys.plugins.repository.AmetysObject; 044import org.ametys.plugins.repository.AmetysObjectResolver; 045import org.ametys.plugins.workspaces.WorkspacesConstants; 046import org.ametys.plugins.workspaces.WorkspacesHelper; 047import org.ametys.plugins.workspaces.about.AboutWorkspaceModule; 048import org.ametys.plugins.workspaces.alert.AlertWorkspaceModule; 049import org.ametys.plugins.workspaces.calendars.CalendarWorkspaceModule; 050import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule; 051import org.ametys.plugins.workspaces.members.MembersWorkspaceModule; 052import org.ametys.plugins.workspaces.minisite.MiniSiteWorkspaceModule; 053import org.ametys.plugins.workspaces.news.NewsWorkspaceModule; 054import org.ametys.plugins.workspaces.project.ProjectConstants; 055import org.ametys.plugins.workspaces.project.ProjectManager; 056import org.ametys.plugins.workspaces.project.ProjectManager.UnknownCatalogSiteException; 057import org.ametys.plugins.workspaces.project.modules.WorkspaceModule; 058import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint; 059import org.ametys.plugins.workspaces.project.objects.Project; 060import org.ametys.plugins.workspaces.wall.WallContentModule; 061import org.ametys.runtime.authentication.AccessDeniedException; 062import org.ametys.runtime.config.Config; 063import org.ametys.runtime.plugin.component.AbstractLogEnabled; 064import org.ametys.web.repository.page.SitemapElement; 065import org.ametys.web.repository.page.ZoneItem; 066import org.ametys.web.repository.page.ZoneItem.ZoneType; 067 068/** 069 * Helper related to rights management for projects. 070 */ 071public class ProjectRightHelper extends AbstractLogEnabled implements Serviceable, Component 072{ 073 /** Avalon Role */ 074 public static final String ROLE = ProjectRightHelper.class.getName(); 075 076 @Obsolete // For v1 project only 077 private static final String __PROJECT_RIGHT_PROFILE = "PROJECT"; 078 079 /** Ametys object resolver */ 080 protected AmetysObjectResolver _resolver; 081 082 /** Project manager */ 083 protected ProjectManager _projectManager; 084 085 /** Right manager */ 086 protected RightManager _rightManager; 087 088 /** Right profiles manager */ 089 protected RightProfilesDAO _rightProfilesDao; 090 091 /** Current user provider */ 092 protected CurrentUserProvider _currentUserProvider; 093 094 /** Workspace Module ExtensionPoint */ 095 protected WorkspaceModuleExtensionPoint _workspaceModuleEP; 096 097 /** Association ContentTypeId, Module */ 098 protected Map<String, WorkspaceModule> _contentTypesToModule; 099 100 /** Module managers EP */ 101 protected WorkspaceModuleExtensionPoint _moduleManagerEP; 102 103 /** Workspace helper */ 104 protected WorkspacesHelper _workspaceHelper; 105 106 private Set<String> _profileIds; 107 108 @Override 109 public void service(ServiceManager manager) throws ServiceException 110 { 111 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 112 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 113 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 114 _rightProfilesDao = (RightProfilesDAO) manager.lookup(RightProfilesDAO.ROLE); 115 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 116 _moduleManagerEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 117 _workspaceHelper = (WorkspacesHelper) manager.lookup(WorkspacesHelper.ROLE); 118 119 _workspaceModuleEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 120 _contentTypesToModule = Map.of( 121 WorkspacesConstants.WALL_CONTENT_CONTENT_TYPE_ID, _workspaceModuleEP.getModule(WallContentModule.WALLCONTENT_MODULE_ID), 122 WorkspacesConstants.PROJECT_NEWS_CONTENT_TYPE_ID, _workspaceModuleEP.getModule(NewsWorkspaceModule.NEWS_MODULE_ID), 123 WorkspacesConstants.PROJECT_ALERT_CONTENT_TYPE_ID, _workspaceModuleEP.getModule(AlertWorkspaceModule.ALERT_MODULE_ID), 124 WorkspacesConstants.PROJECT_ARTICLE_CONTENT_TYPE, _workspaceModuleEP.getModule(MiniSiteWorkspaceModule.MINISITE_MODULE_ID), 125 WorkspacesConstants.ABOUT_CONTENT_TYPE, _workspaceModuleEP.getModule(AboutWorkspaceModule.ABOUT_MODULE_ID) 126 ); 127 } 128 129 /** 130 * The association of all project content types and associated modules 131 * @return The association 132 */ 133 public Map<String, WorkspaceModule> getProjectContentTypesAndModules() 134 { 135 return _contentTypesToModule; 136 } 137 138 /** 139 * Retrieves all project profiles ids given the "profile list" configuration parameter 140 * Profile order is guaranteed to be the same as in the configuration parameter. 141 * @return the projects 142 */ 143 public synchronized Set<String> getProfilesIds() 144 { 145 if (_profileIds == null) 146 { 147 String rawProjectProfileIds = StringUtils.defaultString(Config.getInstance().getValue("workspaces.profile.list")); 148 _profileIds = Arrays.stream(StringUtils.split(rawProjectProfileIds, ',')).collect(Collectors.toSet()); 149 } 150 return _profileIds; 151 } 152 153 /** 154 * Retrieves all project profile given the "profile list" configuration parameter 155 * Profile order is guaranteed to be the same as in the configuration parameter. 156 * @return the projects 157 */ 158 public Set<Profile> getProfiles() 159 { 160 // getProfiles(null) to get only shared profile 161 Map<String, Profile> profileMap = _rightProfilesDao.getProfiles(null).stream().collect(Collectors.toMap(Profile::getId, item -> item)); 162 163 // Collect project profiles (unexisting entries are filtered out). 164 return getProfilesIds().stream() 165 .map(id -> 166 { 167 Profile p = profileMap.get(id); 168 169 // log null entries 170 if (p == null) 171 { 172 getLogger().warn("Could not find profile with id '{}'.", id); 173 } 174 175 return p; 176 }) 177 .filter(Objects::nonNull) 178 .collect(Collectors.toSet()); 179 } 180 181 /** 182 * Get the list of profiles and the list of modules available for rights affectation in the project. 183 * @param projectName The project to check if the modules are activated. Can be null to ignore 184 * @return the project rights data 185 */ 186 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 187 public Map<String, Object> getProjectRightsData(String projectName) 188 { 189 190 Project project = projectName != null ? _projectManager.getProject(projectName) : null; 191 192 if (!canEditMember(project)) 193 { 194 throw new AccessDeniedException("User '" + _currentUserProvider.getUser() + "' tried to do read operation without convenient right"); 195 } 196 197 // profiles 198 List<Object> profiles = getProfiles() 199 .stream() 200 .map(this::_getProfileRightData) 201 .collect(Collectors.toList()); 202 203 204 // modules 205 Stream<Map<String, Object>> stream = _moduleManagerEP.getExtensionsIds().stream().map(moduleId -> _moduleManagerEP.getExtension(moduleId)).map(module -> _getModuleRightData(project, module)); 206 List<Object> modules = stream.filter(Objects::nonNull).collect(Collectors.toList()); 207 208 Map<String, Object> result = new HashMap<>(); 209 result.put("profiles", profiles); 210 result.put("modules", modules); 211 212 return result; 213 } 214 215 private Map<String, Object> _getProfileRightData(Profile profile) 216 { 217 Map<String, Object> data = new HashMap<>(); 218 data.put("id", profile.getId()); 219 data.put("label", profile.getLabel()); 220 return data; 221 } 222 223 private Map<String, Object> _getModuleRightData(Project project, WorkspaceModule module) 224 { 225 if (project != null && !_projectManager.isModuleActivated(project, module.getId())) 226 { 227 return null; 228 } 229 230 Map<String, Object> data = new HashMap<>(); 231 data.put("id", module.getId()); 232 data.put("label", module.getModuleTitle()); 233 return data; 234 } 235 236 /** 237 * Determines if the current user can view the members of a project 238 * @param project the project 239 * @return true if user can view members 240 */ 241 public boolean canViewMembers(Project project) 242 { 243 return _rightManager.currentUserHasReadAccess(project); 244 } 245 246 /** 247 * Determines if the current user has right to add member on project 248 * @param project the project 249 * @return true if user can add member 250 */ 251 public boolean canAddMember(Project project) 252 { 253 MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID); 254 if (project != null && module != null && _projectManager.isModuleActivated(project, module.getId())) 255 { 256 AmetysObject moduleRoot = module.getModuleRoot(project, false); 257 return moduleRoot != null && _rightManager.currentUserHasRight(ProjectConstants.RIGHT_PROJECT_ADD_MEMBER, moduleRoot) == RightResult.RIGHT_ALLOW; 258 } 259 260 return false; 261 } 262 263 /** 264 * Determines if the current user has right to edit member on project 265 * @param project the project 266 * @return true if user can edit member 267 */ 268 public boolean canEditMember(Project project) 269 { 270 return canAddMember(project); 271 } 272 273 /** 274 * Determines if the current user has right to add member on project 275 * @param project the project 276 * @return true if user can remove member 277 */ 278 public boolean canRemoveMember(Project project) 279 { 280 return _hasRightOnMembers(project, ProjectConstants.RIGHT_PROJECT_REMOVE_MEMBER); 281 } 282 283 private boolean _hasRightOnMembers(Project project, String rightId) 284 { 285 MembersWorkspaceModule module = _moduleManagerEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID); 286 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 287 { 288 AmetysObject moduleRoot = module.getModuleRoot(project, false); 289 return moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW; 290 } 291 292 return false; 293 } 294 295 /** 296 * Determines if the current user has right to add tags on project 297 * @param project the project 298 * @return true if user can add tags 299 */ 300 public boolean canAddTag(Project project) 301 { 302 return _hasRightOnTagsOrPlaces(project, ProjectConstants.RIGHT_PROJECT_ADD_TAG); 303 } 304 305 /** 306 * Determines if the current user has right to remove tags on project 307 * @param project the project 308 * @return true if user can remove tags 309 */ 310 public boolean canRemoveTag(Project project) 311 { 312 return _hasRightOnTagsOrPlaces(project, ProjectConstants.RIGHT_PROJECT_DELETE_TAG); 313 } 314 315 private boolean _hasRightOnTagsOrPlaces(Project project, String rightId) 316 { 317 WorkspaceModule module = _moduleManagerEP.getModule(CalendarWorkspaceModule.CALENDAR_MODULE_ID); 318 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 319 { 320 AmetysObject moduleRoot = module.getModuleRoot(project, false); 321 if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW) 322 { 323 return true; 324 } 325 } 326 327 module = _moduleManagerEP.getModule(DocumentWorkspaceModule.DOCUMENT_MODULE_ID); 328 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 329 { 330 AmetysObject moduleRoot = module.getModuleRoot(project, false); 331 if (moduleRoot != null && _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW) 332 { 333 return true; 334 } 335 } 336 337 return false; 338 } 339 340 /** 341 * Determines if current user has read access to module on current project 342 * @param moduleId the module id 343 * @return true if current user has read access, false otherwise 344 */ 345 public boolean hasReadAccessOnModule(String moduleId) 346 { 347 return hasReadAccessOnModule(_workspaceHelper.getProjectFromRequest(), moduleId); 348 } 349 350 /** 351 * Determines if current user has read access to module on given project 352 * @param project the project 353 * @param moduleId the module id 354 * @return true if current user has right, false otherwise 355 */ 356 public boolean hasReadAccessOnModule(Project project, String moduleId) 357 { 358 return hasReadAccessOnModule(project, moduleId, _currentUserProvider.getUser()); 359 } 360 361 /** 362 * Determines if current user has read access to module on given project 363 * @param project the project 364 * @param moduleId the module id 365 * @param userIdentity the user identity 366 * @return true if current user has right, false otherwise 367 */ 368 public boolean hasReadAccessOnModule(Project project, String moduleId, UserIdentity userIdentity) 369 { 370 WorkspaceModule module = _workspaceModuleEP.getModule(moduleId); 371 if (module != null && project != null) 372 { 373 ModifiableResourceCollection moduleRoot = module.getModuleRoot(project, false); 374 if (moduleRoot != null) 375 { 376 return _rightManager.hasReadAccess(userIdentity, moduleRoot); 377 } 378 } 379 return false; 380 } 381 382 /** 383 * Determines if current user has a given right on module on current project 384 * @param rightId the right to check 385 * @param moduleId the module id 386 * @return true if current user has read access, false otherwise 387 */ 388 public boolean hasRightOnModule(String rightId, String moduleId) 389 { 390 return hasRightOnModule(_workspaceHelper.getProjectFromRequest(), rightId, moduleId); 391 } 392 393 /** 394 * Determines if current user has a given right on module on given project 395 * @param project the project 396 * @param rightId the right to check 397 * @param moduleId the module id 398 * @return true if current user has right, false otherwise 399 */ 400 public boolean hasRightOnModule(Project project, String rightId, String moduleId) 401 { 402 WorkspaceModule module = _workspaceModuleEP.getModule(moduleId); 403 if (module != null && project != null) 404 { 405 ModifiableResourceCollection moduleRoot = module.getModuleRoot(project, false); 406 if (moduleRoot != null) 407 { 408 return _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW; 409 } 410 } 411 412 return false; 413 } 414 415 /** 416 * Test if the current user has the right on the project 417 * @param rightId The right id 418 * @param project The project 419 * @return true if has right 420 */ 421 public boolean hasRight(String rightId, Project project) 422 { 423 return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, project) == RightResult.RIGHT_ALLOW; 424 } 425 426 /** 427 * Test if the current user has a read access on current project 428 * @return true if has read access 429 */ 430 public boolean hasReadAccess() 431 { 432 return hasReadAccess(_workspaceHelper.getProjectFromRequest()); 433 } 434 435 /** 436 * Test if the current user has a read access on the project 437 * @param project The project 438 * @return true if has read access 439 */ 440 public boolean hasReadAccess(Project project) 441 { 442 return project != null && _rightManager.hasReadAccess(_currentUserProvider.getUser(), project); 443 } 444 445 /** 446 * Test if the current user has the right on an explorer node 447 * @param rightId The right id 448 * @param explorerNode The explorer node 449 * @return true if has right 450 */ 451 public boolean hasRight(String rightId, ExplorerNode explorerNode) 452 { 453 return _rightManager.hasRight(_currentUserProvider.getUser(), rightId, explorerNode) == RightResult.RIGHT_ALLOW; 454 } 455 456 /** 457 * Test if current user has read access on catalog site 458 * @param zoneItem the zoneItem holding the catalog service. Cannot be null 459 * @return true if current user has read access 460 */ 461 public boolean hasCatalogReadAccess(ZoneItem zoneItem) 462 { 463 SitemapElement sitemapElement = zoneItem.getZone().getSitemapElement(); 464 return _isCatalogService(zoneItem) && _rightManager.currentUserHasReadAccess(sitemapElement); 465 } 466 467 private boolean _isCatalogService(ZoneItem zoneItem) 468 { 469 try 470 { 471 SitemapElement page = zoneItem.getZone().getSitemapElement(); 472 if (page.getSiteName().equals(_projectManager.getCatalogSiteName())) 473 { 474 return zoneItem.getType() == ZoneType.SERVICE && "org.ametys.plugins.workspaces.service.ProjectsCatalogue".equals(zoneItem.getServiceId()); 475 } 476 } 477 catch (UnknownCatalogSiteException e) 478 { 479 // Ignore the no catalog sitename exception 480 } 481 482 return false; 483 } 484}