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.members; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Objects; 027import java.util.Optional; 028import java.util.Set; 029import java.util.function.Predicate; 030import java.util.stream.Collectors; 031 032import org.apache.avalon.framework.component.Component; 033import org.apache.avalon.framework.context.Context; 034import org.apache.avalon.framework.context.ContextException; 035import org.apache.avalon.framework.context.Contextualizable; 036import org.apache.avalon.framework.service.ServiceException; 037import org.apache.avalon.framework.service.ServiceManager; 038import org.apache.avalon.framework.service.Serviceable; 039import org.apache.cocoon.components.ContextHelper; 040import org.apache.cocoon.environment.Request; 041import org.apache.commons.lang3.StringUtils; 042 043import org.ametys.core.group.Group; 044import org.ametys.core.group.GroupIdentity; 045import org.ametys.core.group.GroupManager; 046import org.ametys.core.observation.Event; 047import org.ametys.core.observation.ObservationManager; 048import org.ametys.core.right.Profile; 049import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint; 050import org.ametys.core.right.RightManager; 051import org.ametys.core.right.RightProfilesDAO; 052import org.ametys.core.ui.Callable; 053import org.ametys.core.user.CurrentUserProvider; 054import org.ametys.core.user.User; 055import org.ametys.core.user.UserIdentity; 056import org.ametys.core.user.UserManager; 057import org.ametys.plugins.core.user.UserHelper; 058import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 059import org.ametys.plugins.repository.AmetysObject; 060import org.ametys.plugins.repository.AmetysObjectIterable; 061import org.ametys.plugins.repository.AmetysObjectResolver; 062import org.ametys.plugins.repository.AmetysRepositoryException; 063import org.ametys.plugins.repository.ModifiableAmetysObject; 064import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 065import org.ametys.plugins.repository.RepositoryConstants; 066import org.ametys.plugins.workspaces.ObservationConstants; 067import org.ametys.plugins.workspaces.members.JCRProjectMember.MemberType; 068import org.ametys.plugins.workspaces.project.ProjectManager; 069import org.ametys.plugins.workspaces.project.modules.WorkspaceModule; 070import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint; 071import org.ametys.plugins.workspaces.project.objects.Project; 072import org.ametys.plugins.workspaces.project.objects.Project.InscriptionStatus; 073import org.ametys.plugins.workspaces.project.rights.ProjectRightHelper; 074import org.ametys.runtime.plugin.component.AbstractLogEnabled; 075import org.ametys.web.population.PopulationContextHelper; 076import org.ametys.web.repository.page.ModifiablePage; 077import org.ametys.web.repository.page.Page; 078import org.ametys.web.repository.site.Site; 079 080import com.google.common.collect.ImmutableList; 081 082/** 083 * Helper component for managing project's users 084 */ 085public class ProjectMemberManager extends AbstractLogEnabled implements Serviceable, Component, Contextualizable 086{ 087 /** Avalon Role */ 088 public static final String ROLE = ProjectMemberManager.class.getName(); 089 090 /** The id of the members service */ 091 public static final String __WORKSPACES_SERVICE_MEMBERS = "org.ametys.plugins.workspaces.module.Members"; 092 093 /** Constants for users project node */ 094 private static final String __PROJECT_MEMBERS_NODE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":members"; 095 096 /** The type of the project users node type */ 097 private static final String __PROJECT_MEMBERS_NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":unstructured"; 098 099 /** The type of a project user node type */ 100 private static final String __PROJECT_MEMBER_NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":project-member"; 101 102 /** Project Right to add a member to a project */ 103 private static final String __RIGHTS_ADD_MEMBER_TO_PROJECT = "Plugins_Workspaces_Rights_Service_Module_Members_Add"; 104 105 /** Project Right to remove a member from the project */ 106 private static final String __RIGHTS_REMOVE_MEMBER_TO_PROJECT = "Plugins_Workspaces_Rights_Service_Module_Members_Remove"; 107 108 private static final String __PROJECT_RIGHT_PROFILE = "PROJECT"; 109 110 /** Avalon context */ 111 protected Context _context; 112 113 /** Project manager */ 114 protected ProjectManager _projectManager; 115 116 /** Project rights helper */ 117 protected ProjectRightHelper _projectRightHelper; 118 119 /** Profiles right manager */ 120 protected RightProfilesDAO _rightProfilesDAO; 121 122 /** Profile assignment storage */ 123 protected ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageExtensionPoint; 124 125 /** Ametys object resolver */ 126 protected AmetysObjectResolver _resolver; 127 128 /** Rights manager */ 129 protected RightManager _rightManager; 130 131 /** Current user provider */ 132 protected CurrentUserProvider _currentUserProvider; 133 134 /** Users manager */ 135 protected UserManager _userManager; 136 137 /** The observation manager */ 138 protected ObservationManager _observationManager; 139 140 /** Module managers EP */ 141 protected WorkspaceModuleExtensionPoint _moduleManagerEP; 142 143 /** The user helper */ 144 protected UserHelper _userHelper; 145 146 /** The groups manager */ 147 protected GroupManager _groupManager; 148 149 private PopulationContextHelper _populationContextHelper; 150 151 @Override 152 public void contextualize(Context context) throws ContextException 153 { 154 _context = context; 155 } 156 157 @Override 158 public void service(ServiceManager manager) throws ServiceException 159 { 160 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 161 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 162 _projectRightHelper = (ProjectRightHelper) manager.lookup(ProjectRightHelper.ROLE); 163 _rightProfilesDAO = (RightProfilesDAO) manager.lookup(RightProfilesDAO.ROLE); 164 _profileAssignmentStorageExtensionPoint = (ProfileAssignmentStorageExtensionPoint) manager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE); 165 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 166 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 167 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 168 _groupManager = (GroupManager) manager.lookup(GroupManager.ROLE); 169 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 170 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 171 _moduleManagerEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 172 _populationContextHelper = (PopulationContextHelper) manager.lookup(org.ametys.core.user.population.PopulationContextHelper.ROLE); 173 } 174 175 /** 176 * Retrieve the data of a member of a project, or the default data if no user is provided 177 * @param projectName The name of the project 178 * @param identity The user or group identity. If null, return the default profiles for a new user 179 * @param type The type of the identity. Can be "user" or "group" 180 * @return The map of profiles per module for the user 181 * @throws IllegalAccessException If the user cannot execute this operation 182 */ 183 @Callable 184 public Map<String, Object> getProjectMemberData(String projectName, String identity, String type) throws IllegalAccessException 185 { 186 Map<String, Object> result = new HashMap<>(); 187 188 boolean isTypeUser = JCRProjectMember.MemberType.USER.toString().equals(type); 189 boolean isTypeGroup = JCRProjectMember.MemberType.GROUP.toString().equals(type); 190 UserIdentity user = Optional.ofNullable(identity) 191 .filter(id -> id != null && isTypeUser) 192 .map(UserIdentity::stringToUserIdentity) 193 .orElse(null); 194 GroupIdentity group = Optional.ofNullable(identity) 195 .filter(id -> id != null && isTypeGroup) 196 .map(GroupIdentity::stringToGroupIdentity) 197 .orElse(null); 198 199 if (identity != null) 200 { 201 if (isTypeGroup && group == null) 202 { 203 result.put("message", "unknow-group"); 204 return result; 205 } 206 else if (isTypeUser && user == null) 207 { 208 result.put("message", "unknow-user"); 209 return result; 210 } 211 } 212 213 Project project = _projectManager.getProject(projectName); 214 if (project == null) 215 { 216 result.put("message", "unknow-project"); 217 return result; 218 } 219 220 if (!_projectRightHelper.hasRight(__RIGHTS_ADD_MEMBER_TO_PROJECT, project)) 221 { 222 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to access a privilege feature without convenient right [" + __RIGHTS_ADD_MEMBER_TO_PROJECT + ", /projects" + project.getPath() + "]"); 223 } 224 225 boolean newMember = true; 226 Map<String, String> userProfiles; 227 228 if (user != null || group != null) 229 { 230 JCRProjectMember projectMember = user != null ? getOrCreateProjectMember(project, user) : getOrCreateProjectMember(project, group); 231 232 newMember = projectMember.needsSave(); 233 234 String role = projectMember.getRole(); 235 if (role != null) 236 { 237 result.put("role", role); 238 } 239 240 userProfiles = _getMemberProfiles(projectMember, project); 241 } 242 else 243 { 244 userProfiles = new HashMap<>(); 245 } 246 247 result.put("profiles", userProfiles); 248 result.put("status", newMember ? "new" : "edit"); 249 250 return result; 251 } 252 253 private Map<String, String> _getMemberProfiles(JCRProjectMember member, Project project) 254 { 255 Map<String, String> userProfiles = new HashMap<>(); 256 257 List<String> profileIds = _projectRightHelper.getProfileList() 258 .stream() 259 .map(p -> p.getId()) 260 .collect(Collectors.toList()); 261 262 // Get allowed profile on modules (among the project members's profiles) 263 for (WorkspaceModule module : _projectManager.getModules(project)) 264 { 265 AmetysObject moduleObject = module.getModuleRoot(project, false); 266 Set<String> allowedProfilesForUser = MemberType.GROUP.toString().equals(member.getType()) 267 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(moduleObject, member.getGroup()) 268 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(moduleObject, member.getUser()); 269 270 for (String allowedProfile : allowedProfilesForUser) 271 { 272 if (profileIds.contains(allowedProfile)) 273 { 274 // Get the first allowed profile among the project's members profiles 275 userProfiles.put(module.getId(), allowedProfile); 276 break; 277 } 278 } 279 280 if (!userProfiles.containsKey(module.getId())) 281 { 282 userProfiles.put(module.getId(), null); 283 } 284 } 285 286 // Get allowed profile on project (among the project members's profiles) 287 Set<String> allowedProfilesOnProject = MemberType.GROUP.toString().equals(member.getType()) 288 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(project, member.getGroup()) 289 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(project, member.getUser()); 290 for (String allowedProfile : allowedProfilesOnProject) 291 { 292 if (profileIds.contains(allowedProfile)) 293 { 294 // Get the first allowed profile among the project's members profiles 295 userProfiles.put(__PROJECT_RIGHT_PROFILE, allowedProfile); 296 break; 297 } 298 } 299 300 if (!userProfiles.containsKey(__PROJECT_RIGHT_PROFILE)) 301 { 302 userProfiles.put(__PROJECT_RIGHT_PROFILE, null); 303 } 304 return userProfiles; 305 } 306 307 /** 308 * Set the user data in the project 309 * @param projectName The project name 310 * @param identity The user or group identity. 311 * @param type The type of the identity. Can be "user" or "group" 312 * @param newProfiles The profiles to affect, mapped by module 313 * @param role The user role inside the project 314 * @return The result 315 * @throws IllegalAccessException If the user cannot execute this operation 316 */ 317 @Callable 318 public Map<String, Object> setProjectMemberData(String projectName, String identity, String type, Map<String, String> newProfiles, String role) throws IllegalAccessException 319 { 320 Map<String, Object> result = new HashMap<>(); 321 322 boolean isTypeUser = JCRProjectMember.MemberType.USER.toString().equals(type); 323 boolean isTypeGroup = JCRProjectMember.MemberType.GROUP.toString().equals(type); 324 UserIdentity user = Optional.ofNullable(identity) 325 .filter(id -> id != null && isTypeUser) 326 .map(UserIdentity::stringToUserIdentity) 327 .orElse(null); 328 GroupIdentity group = Optional.ofNullable(identity) 329 .filter(id -> id != null && isTypeGroup) 330 .map(GroupIdentity::stringToGroupIdentity) 331 .orElse(null); 332 333 if (group == null && user == null) 334 { 335 result.put("message", isTypeGroup ? "unknow-group" : "unknow-user"); 336 return result; 337 } 338 339 340 Project project = _projectManager.getProject(projectName); 341 if (project == null) 342 { 343 result.put("message", "unknow-project"); 344 return result; 345 } 346 347 if (!_projectRightHelper.hasRight(__RIGHTS_ADD_MEMBER_TO_PROJECT, project)) 348 { 349 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to access a privilege feature without convenient right [" + __RIGHTS_ADD_MEMBER_TO_PROJECT + ", /projects" + project.getPath() + "]"); 350 } 351 352 JCRProjectMember projectMember = isTypeUser ? getOrCreateProjectMember(project, user) : getOrCreateProjectMember(project, group); 353 boolean newMember = projectMember.needsSave(); 354 355 if (role != null) 356 { 357 projectMember.setRole(role); 358 } 359 360 _setMemberProfiles(newProfiles, projectMember, project, newMember); 361 362 project.saveChanges(); 363 364 if (newMember) 365 { 366 // Notify listeners 367 Map<String, Object> eventParams = new HashMap<>(); 368 eventParams.put(ObservationConstants.ARGS_MEMBER, projectMember); 369 eventParams.put(ObservationConstants.ARGS_MEMBER_ID, projectMember.getId()); 370 eventParams.put(ObservationConstants.ARGS_PROJECT, project); 371 eventParams.put(ObservationConstants.ARGS_PROJECT_ID, project.getId()); 372 _observationManager.notify(new Event(ObservationConstants.EVENT_MEMBER_ADDED, _currentUserProvider.getUser(), eventParams)); 373 } 374 375 return result; 376 } 377 378 /** 379 * Add a user to a project with open inscriptions, using the default values 380 * @param project The project 381 * @param user The user 382 * @return True if the user was successfully added 383 */ 384 public boolean addProjectMember(Project project, UserIdentity user) 385 { 386 if (user == null) 387 { 388 return false; 389 } 390 391 InscriptionStatus inscriptionStatus = project.getInscriptionStatus(); 392 if (!inscriptionStatus.equals(InscriptionStatus.OPEN)) 393 { 394 return false; 395 } 396 397 JCRProjectMember projectMember = getOrCreateProjectMember(project, user); 398 399 // Allow the user to access the project's site 400 _allowReadAccessOnProjectDashboard(projectMember, project); 401 List<Profile> projectProfiles = _projectRightHelper.getProfileList(); 402 List<String> allowedProfiles = new ArrayList<>(); 403 String defaultProfile = project.getDefaultProfile(); 404 if (defaultProfile != null) 405 { 406 allowedProfiles.add(defaultProfile); 407 } 408 allowedProfiles.add(RightManager.READER_PROFILE_ID); 409 _setMemberProfiles(projectMember, projectProfiles, allowedProfiles, project); 410 411 for (WorkspaceModule module : _moduleManagerEP.getModules()) 412 { 413 if (module != null && _projectManager.isModuleActivated(project, module.getId())) 414 { 415 AmetysObject moduleObject = module.getModuleRoot(project, false); 416 _setMemberProfiles(projectMember, projectProfiles, allowedProfiles, moduleObject); 417 418 // Update read access on project module's pages 419 AmetysObjectIterable<Page> modulePages = module.getModulePages(project, null); 420 for (Page modulePage : modulePages) 421 { 422 _updateReadAccessOnModulePage(projectMember, modulePage, allowedProfiles.size() > 0); 423 } 424 } 425 } 426 427 project.saveChanges(); 428 429 // Notify listeners 430 Map<String, Object> eventParams = new HashMap<>(); 431 eventParams.put(ObservationConstants.ARGS_MEMBER, projectMember); 432 eventParams.put(ObservationConstants.ARGS_MEMBER_ID, projectMember.getId()); 433 eventParams.put(ObservationConstants.ARGS_PROJECT, project); 434 eventParams.put(ObservationConstants.ARGS_PROJECT_ID, project.getId()); 435 _observationManager.notify(new Event(ObservationConstants.EVENT_MEMBER_ADDED, _currentUserProvider.getUser(), eventParams)); 436 437 return true; 438 } 439 440 private void _setMemberProfiles(Map<String, String> newProfiles, JCRProjectMember projectMember, Project project, boolean newMember) 441 { 442 // Allow the user to access the project's site 443 _allowReadAccessOnProjectDashboard(projectMember, project); 444 445 List<Profile> projectProfiles = _projectRightHelper.getProfileList(); 446 for (Map.Entry<String, String> entry : newProfiles.entrySet()) 447 { 448 List<String> allowedProfiles = new ArrayList<>(); 449 if (entry.getValue() != null) 450 { 451 allowedProfiles.add(entry.getValue()); 452 } 453 454 String moduleId = entry.getKey(); 455 if (__PROJECT_RIGHT_PROFILE.equals(moduleId)) 456 { 457 if (newMember) 458 { 459 allowedProfiles.add(RightManager.READER_PROFILE_ID); 460 } 461 _setMemberProfiles(projectMember, projectProfiles, allowedProfiles, project); 462 } 463 else 464 { 465 WorkspaceModule module = _moduleManagerEP.getModule(moduleId); 466 if (module != null && _projectManager.isModuleActivated(project, moduleId)) 467 { 468 AmetysObject moduleObject = module.getModuleRoot(project, false); 469 _setMemberProfiles(projectMember, projectProfiles, allowedProfiles, moduleObject); 470 471 // Update read access on project module's pages 472 AmetysObjectIterable<Page> modulePages = module.getModulePages(project, null); 473 for (Page modulePage : modulePages) 474 { 475 _updateReadAccessOnModulePage(projectMember, modulePage, allowedProfiles.size() > 0); 476 } 477 } 478 } 479 } 480 } 481 482 private void _setMemberProfiles(JCRProjectMember member, List<Profile> projectProfiles, List<String> allowedProfiles, AmetysObject object) 483 { 484 Set<String> updatedProfiles = new HashSet<>(); 485 486 Set<String> currentAllowedProfiles = MemberType.GROUP.toString().equals(member.getType()) 487 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(object, member.getGroup()) 488 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(object, member.getUser()); 489 490 if (allowedProfiles.size() == 0) 491 { 492 _removeMemberProfiles(member, projectProfiles, object, updatedProfiles, currentAllowedProfiles); 493 } 494 else 495 { 496 _setMemberProfiles(member, projectProfiles, allowedProfiles, object, updatedProfiles, currentAllowedProfiles); 497 } 498 499 if (updatedProfiles.size() > 0) 500 { 501 Map<String, Object> eventParams = new HashMap<>(); 502 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT, object); 503 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, object.getId()); 504 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_PROFILES, updatedProfiles); 505 506 _observationManager.notify(new Event(org.ametys.core.ObservationConstants.EVENT_ACL_UPDATED, _currentUserProvider.getUser(), eventParams)); 507 } 508 } 509 510 private void _setMemberProfiles(JCRProjectMember member, List<Profile> projectProfiles, List<String> allowedProfiles, AmetysObject object, Set<String> updatedProfiles, Set<String> currentAllowedProfiles) 511 { 512 // Reset the setted project's profiles 513 for (Profile profile : projectProfiles) 514 { 515 if (currentAllowedProfiles.contains(profile.getId()) && !allowedProfiles.contains(profile.getId())) 516 { 517 if (MemberType.GROUP.toString().equals(member.getType())) 518 { 519 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromGroup(member.getGroup(), profile.getId(), object); 520 } 521 else 522 { 523 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(member.getUser(), profile.getId(), object); 524 } 525 updatedProfiles.add(profile.getId()); 526 } 527 } 528 529 for (String allowedProfile : allowedProfiles) 530 { 531 if (!currentAllowedProfiles.contains(allowedProfile)) 532 { 533 // Set the allowed profile 534 if (MemberType.GROUP.toString().equals(member.getType())) 535 { 536 _profileAssignmentStorageExtensionPoint.allowProfileToGroup(member.getGroup(), allowedProfile, object); 537 } 538 else 539 { 540 _profileAssignmentStorageExtensionPoint.allowProfileToUser(member.getUser(), allowedProfile, object); 541 } 542 updatedProfiles.add(allowedProfile); 543 } 544 } 545 546 if (!currentAllowedProfiles.contains(RightManager.READER_PROFILE_ID) && !allowedProfiles.contains(RightManager.READER_PROFILE_ID)) 547 { 548 // Allow the READER profiles 549 if (MemberType.GROUP.toString().equals(member.getType())) 550 { 551 _profileAssignmentStorageExtensionPoint.allowProfileToGroup(member.getGroup(), RightManager.READER_PROFILE_ID, object); 552 } 553 else 554 { 555 _profileAssignmentStorageExtensionPoint.allowProfileToUser(member.getUser(), RightManager.READER_PROFILE_ID, object); 556 } 557 updatedProfiles.add(RightManager.READER_PROFILE_ID); 558 } 559 } 560 561 private void _removeMemberProfiles(JCRProjectMember member, List<Profile> projectProfiles, AmetysObject object, Set<String> updatedProfiles, Set<String> currentAllowedProfiles) 562 { 563 // Reset the setted project's profiles 564 for (Profile profile : projectProfiles) 565 { 566 if (currentAllowedProfiles.contains(profile.getId())) 567 { 568 if (MemberType.GROUP.toString().equals(member.getType())) 569 { 570 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromGroup(member.getGroup(), profile.getId(), object); 571 } 572 else 573 { 574 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(member.getUser(), profile.getId(), object); 575 } 576 updatedProfiles.add(profile.getId()); 577 } 578 } 579 580 // Remove the READER profile 581 if (currentAllowedProfiles.contains(RightManager.READER_PROFILE_ID)) 582 { 583 if (MemberType.GROUP.toString().equals(member.getType())) 584 { 585 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromGroup(member.getGroup(), RightManager.READER_PROFILE_ID, object); 586 } 587 else 588 { 589 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(member.getUser(), RightManager.READER_PROFILE_ID, object); 590 } 591 updatedProfiles.add(RightManager.READER_PROFILE_ID); 592 } 593 } 594 595 private void _allowReadAccessOnProjectDashboard(JCRProjectMember member, Project project) 596 { 597 // Add READER profile for all dashboards of project 598 for (ModifiablePage dashboardPage : _getProjectDashboardPages(project)) 599 { 600 Set<String> currentAllowedProfiles = MemberType.GROUP.toString().equals(member.getType()) 601 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(dashboardPage, member.getGroup()) 602 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(dashboardPage, member.getUser()); 603 if (!currentAllowedProfiles.contains(RightManager.READER_PROFILE_ID)) 604 { 605 if (MemberType.GROUP.toString().equals(member.getType())) 606 { 607 _profileAssignmentStorageExtensionPoint.allowProfileToGroup(member.getGroup(), RightManager.READER_PROFILE_ID, dashboardPage); 608 } 609 else 610 { 611 _profileAssignmentStorageExtensionPoint.allowProfileToUser(member.getUser(), RightManager.READER_PROFILE_ID, dashboardPage); 612 } 613 614 dashboardPage.saveChanges(); 615 616 Map<String, Object> eventParams = new HashMap<>(); 617 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT, dashboardPage); 618 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, dashboardPage.getId()); 619 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_PROFILES, Collections.singleton(RightManager.READER_PROFILE_ID)); 620 621 _observationManager.notify(new Event(org.ametys.core.ObservationConstants.EVENT_ACL_UPDATED, MemberType.GROUP.toString().equals(member.getType()) ? null : member.getUser(), eventParams)); 622 } 623 } 624 } 625 626 private List<ModifiablePage> _getProjectDashboardPages(Project project) 627 { 628 List<ModifiablePage> dashboardPages = project.getSites() 629 .stream() 630 .map(Site::getSitemaps) 631 .flatMap(AmetysObjectIterable::stream) 632 .map(sitemap -> _projectManager.getProjectDashboardPage(project, sitemap.getSitemapName())) 633 .flatMap(AmetysObjectIterable::stream) 634 .filter(page -> page instanceof ModifiablePage) 635 .map(ModifiablePage.class::cast) 636 .collect(Collectors.toList()); 637 return dashboardPages; 638 } 639 640 private void _removeReadAccessOnProjectDashboard(JCRProjectMember member, Project project) 641 { 642 // Add READER profile for all dashboard pages of project 643 for (ModifiablePage dashboardPage : _getProjectDashboardPages(project)) 644 { 645 Set<String> currentAllowedProfiles = MemberType.GROUP.toString().equals(member.getType()) 646 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(dashboardPage, member.getGroup()) 647 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(dashboardPage, member.getUser()); 648 if (currentAllowedProfiles.contains(RightManager.READER_PROFILE_ID)) 649 { 650 if (MemberType.GROUP.toString().equals(member.getType())) 651 { 652 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromGroup(member.getGroup(), RightManager.READER_PROFILE_ID, dashboardPage); 653 } 654 else 655 { 656 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(member.getUser(), RightManager.READER_PROFILE_ID, dashboardPage); 657 } 658 659 dashboardPage.saveChanges(); 660 661 Map<String, Object> eventParams = new HashMap<>(); 662 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT, dashboardPage); 663 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, dashboardPage.getId()); 664 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_PROFILES, Collections.singleton(RightManager.READER_PROFILE_ID)); 665 666 _observationManager.notify(new Event(org.ametys.core.ObservationConstants.EVENT_ACL_UPDATED, _currentUserProvider.getUser(), eventParams)); 667 } 668 } 669 } 670 671 private void _updateReadAccessOnModulePage(JCRProjectMember member, Page modulePage, boolean allowed) 672 { 673 Set<String> currentAllowedProfiles = MemberType.GROUP.toString().equals(member.getType()) 674 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(modulePage, member.getGroup()) 675 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(modulePage, member.getUser()); 676 677 boolean hasChanges = false; 678 679 if (allowed) 680 { 681 if (!currentAllowedProfiles.contains(RightManager.READER_PROFILE_ID)) 682 { 683 if (MemberType.GROUP.toString().equals(member.getType())) 684 { 685 _profileAssignmentStorageExtensionPoint.allowProfileToGroup(member.getGroup(), RightManager.READER_PROFILE_ID, modulePage); 686 } 687 else 688 { 689 _profileAssignmentStorageExtensionPoint.allowProfileToUser(member.getUser(), RightManager.READER_PROFILE_ID, modulePage); 690 } 691 hasChanges = true; 692 } 693 } 694 else 695 { 696 // remove READER profile on module page 697 if (currentAllowedProfiles.contains(RightManager.READER_PROFILE_ID)) 698 { 699 if (MemberType.GROUP.toString().equals(member.getType())) 700 { 701 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromGroup(member.getGroup(), RightManager.READER_PROFILE_ID, modulePage); 702 } 703 else 704 { 705 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(member.getUser(), RightManager.READER_PROFILE_ID, modulePage); 706 } 707 hasChanges = true; 708 } 709 } 710 711 if (hasChanges) 712 { 713 ((ModifiablePage) modulePage).saveChanges(); 714 715 Map<String, Object> eventParams = new HashMap<>(); 716 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT, modulePage); 717 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, modulePage.getId()); 718 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_PROFILES, Collections.singleton(RightManager.READER_PROFILE_ID)); 719 720 _observationManager.notify(new Event(org.ametys.core.ObservationConstants.EVENT_ACL_UPDATED, _currentUserProvider.getUser(), eventParams)); 721 } 722 } 723 724 private void _removeMemberProfiles(JCRProjectMember member, AmetysObject object) 725 { 726 Set<String> currentAllowedProfiles = MemberType.GROUP.toString().equals(member.getType()) 727 ? _profileAssignmentStorageExtensionPoint.getAllowedProfilesForGroup(object, member.getGroup()) 728 : _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(object, member.getUser()); 729 730 Set<String> removedProfiles = new HashSet<>(); 731 732 if (currentAllowedProfiles.size() > 0) 733 { 734 for (String allowedProfile : currentAllowedProfiles) 735 { 736 if (MemberType.GROUP.toString().equals(member.getType())) 737 { 738 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromGroup(member.getGroup(), allowedProfile, object); 739 } 740 else 741 { 742 _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(member.getUser(), allowedProfile, object); 743 } 744 removedProfiles.add(allowedProfile); 745 } 746 } 747 748 if (removedProfiles.size() > 0) 749 { 750 ((ModifiableAmetysObject) object).saveChanges(); 751 752 Map<String, Object> eventParams = new HashMap<>(); 753 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT, object); 754 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, object.getId()); 755 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_PROFILES, removedProfiles); 756 757 _observationManager.notify(new Event(org.ametys.core.ObservationConstants.EVENT_ACL_UPDATED, _currentUserProvider.getUser(), eventParams)); 758 } 759 } 760 761 /** 762 * Get the current user information 763 * @return The user 764 */ 765 @Callable 766 public Map<String, Object> getCurrentUser() 767 { 768 Map<String, Object> result = new HashMap<>(); 769 result.put("user", _userHelper.user2json(_currentUserProvider.getUser())); 770 return result; 771 } 772 773 /** 774 * Get the members of current project or all the members of all projects in where is no current project 775 * @return The members 776 */ 777 @Callable 778 public Map<String, Object> getProjectMembers() 779 { 780 Map<String, Object> result = new HashMap<>(); 781 782 Request request = ContextHelper.getRequest(_context); 783 String projectName = (String) request.getAttribute("projectName"); 784 785 Collection<Project> projects = new ArrayList<>(); 786 787 if (StringUtils.isNotEmpty(projectName)) 788 { 789 projects.add(_projectManager.getProject(projectName)); 790 } 791 else 792 { 793 _projectManager.getProjects() 794 .stream() 795 .forEach(project -> projects.add(project)); 796 } 797 798 Set<String> projectsPopulations = projects.stream() 799 .map(project -> project.getSites()) 800 .flatMap(Collection::stream) 801 .map(site -> _populationContextHelper.getUserPopulationsOnContext("/sites/" + site.getName(), false)) 802 .flatMap(Set::stream) 803 .collect(Collectors.toSet()); 804 805 Set<JCRProjectMember> members = projects.stream() 806 .map(project -> getProjectMembers(project)) 807 .flatMap(Set::stream) 808 .collect(Collectors.toSet()); 809 810 Set<UserIdentity> users = members.stream() 811 .filter(member -> MemberType.USER.toString().equals(member.getType())) 812 .map(JCRProjectMember::getUser) 813 .collect(Collectors.toSet()); 814 815 users.addAll(members.stream() 816 .filter(member -> MemberType.GROUP.toString().equals(member.getType())) 817 .map(member -> _groupManager.getGroup(member.getGroup())) 818 .filter(Objects::nonNull) 819 .map(group -> group.getUsers()) 820 .flatMap(Set::stream) 821 .filter(user -> projectsPopulations.contains(user.getPopulationId())) 822 .collect(Collectors.toSet())); 823 824 result.put("users", users.stream() 825 .map(identity -> _userHelper.getUser(identity)) 826 .filter(Objects::nonNull) 827 .map(user -> _userHelper.user2json(user, true)) 828 .collect(Collectors.toList())); 829 830 return result; 831 } 832 833 /** 834 * Get the list of users of the project 835 * @param projectName The project name 836 * @return The list of users 837 * @throws AmetysRepositoryException If an error occurred 838 * @throws IllegalAccessException If an error occurred 839 */ 840 @Callable 841 public Map<String, Object> getProjectMembers(String projectName) throws IllegalAccessException, AmetysRepositoryException 842 { 843 Project project = _projectManager.getProject(projectName); 844 Set<JCRProjectMember> members = getProjectMembers(project); 845 List<Map<String, Object>> membersList = new ArrayList<>(); 846 847 if (!_projectRightHelper.hasReadAccess(project)) 848 { 849 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to access a privilege feature without reader right in the project " + project.getPath()); 850 } 851 852 for (JCRProjectMember projectMember : members) 853 { 854 Map<String, Object> memberData = new HashMap<>(); 855 boolean memberExists = false; 856 if (MemberType.USER.toString().equals(projectMember.getType())) 857 { 858 UserIdentity userIdentity = projectMember.getUser(); 859 User user = _userManager.getUser(userIdentity); 860 if (user != null) 861 { 862 memberData.putAll(_userHelper.user2json(user)); 863 memberData.put("type", MemberType.USER.toString()); 864 memberData.put("id", UserIdentity.userIdentityToString(userIdentity)); 865 memberExists = true; 866 } 867 } 868 else if (MemberType.GROUP.toString().equals(projectMember.getType())) 869 { 870 GroupIdentity groupIdentity = projectMember.getGroup(); 871 Group group = _groupManager.getGroup(groupIdentity); 872 if (group != null) 873 { 874 memberData.putAll(group2Json(group)); 875 memberData.put("type", MemberType.GROUP.toString()); 876 memberExists = true; 877 } 878 } 879 880 if (memberExists) 881 { 882 String role = projectMember.getRole(); 883 if (role != null) 884 { 885 memberData.put("role", role); 886 } 887 888 Map<String, Object> rights = new HashMap<>(); 889 890 rights.put("edit", _projectRightHelper.hasRight(__RIGHTS_ADD_MEMBER_TO_PROJECT, project)); 891 rights.put("delete", _projectRightHelper.hasRight(__RIGHTS_REMOVE_MEMBER_TO_PROJECT, project)); 892 memberData.put("rights", rights); 893 894 membersList.add(memberData); 895 } 896 } 897 898 Map<String, Object> result = new HashMap<>(); 899 result.put("members", membersList); 900 901 return result; 902 } 903 904 /** 905 * Retrieves the rights for the current user in the project 906 * @param projectName The project Name 907 * @return The project 908 */ 909 @Callable 910 public Map<String, Object> getMemberModuleRights(String projectName) 911 { 912 Map<String, Object> rights = new HashMap<>(); 913 914 Project project = _projectManager.getProject(projectName); 915 if (project == null) 916 { 917 rights.put("message", "unknow-project"); 918 return rights; 919 } 920 rights.put("view", _projectRightHelper.hasReadAccess(project)); 921 rights.put("add", _projectRightHelper.hasRight(__RIGHTS_ADD_MEMBER_TO_PROJECT, project)); 922 923 return rights; 924 } 925 926 /** 927 * Get the list of users of the project 928 * @param project The project 929 * @return The list of users 930 */ 931 public Set<JCRProjectMember> getProjectMembers(Project project) 932 { 933 Set<JCRProjectMember> projectUsers = new HashSet<>(); 934 935 if (project != null) 936 { 937 ModifiableTraversableAmetysObject usersNode = _getProjectMembersNode(project); 938 939 for (AmetysObject userNode : usersNode.getChildren()) 940 { 941 if (userNode instanceof JCRProjectMember) 942 { 943 projectUsers.add((JCRProjectMember) userNode); 944 } 945 } 946 } 947 948 return projectUsers; 949 } 950 951 /** 952 * Test if an user is a member of a project 953 * @param project The project 954 * @param userIdentity The user identity 955 * @return True if this user is a member of this project 956 */ 957 public boolean isProjectMember(Project project, UserIdentity userIdentity) 958 { 959 if (userIdentity == null) 960 { 961 return false; 962 } 963 964 Set<JCRProjectMember> members = getProjectMembers(project); 965 966 boolean isMember = members.stream() 967 .filter(member -> MemberType.USER.toString().equals(member.getType())) 968 .anyMatch(member -> userIdentity.equals(member.getUser())); 969 970 if (!isMember) 971 { 972 Set<String> projectPopulations = project.getSites() 973 .stream() 974 .map(site -> _populationContextHelper.getUserPopulationsOnContexts(ImmutableList.of("/sites/" + site.getName(), "/sites-fo/" + site.getName()), false, false)) 975 .flatMap(Set::stream) 976 .collect(Collectors.toSet()); 977 978 isMember = members.stream() 979 .filter(member -> MemberType.GROUP.toString().equals(member.getType())) 980 .map(member -> _groupManager.getGroup(member.getGroup())) 981 .filter(Objects::nonNull) 982 .map(group -> group.getUsers()) 983 .flatMap(Set::stream) 984 .filter(user -> projectPopulations.contains(user.getPopulationId())) 985 .anyMatch(member -> userIdentity.equals(member)); 986 } 987 return isMember; 988 } 989 990 /** 991 * Set the manager of a project 992 * @param projectName The project name 993 * @param profileId The profile id to affect 994 * @param userIdentity The user 995 */ 996 public void setProjectManager(String projectName, String profileId, UserIdentity userIdentity) 997 { 998 Project project = _projectManager.getProject(projectName); 999 if (project != null) 1000 { 1001 UserIdentity previousManager = project.getManager(); 1002 if (previousManager != null && previousManager != userIdentity) 1003 { 1004 _removeManagerRights(project, previousManager); 1005 } 1006 else 1007 { 1008 // Remove obsolete rights if the profile changed but the manager stayed the same 1009 _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(project, userIdentity).stream() 1010 .filter(previousProfile -> !profileId.equals(previousProfile) && !RightManager.READER_PROFILE_ID.equals(previousProfile)) 1011 .forEach(obsoleteProfile -> _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(userIdentity, obsoleteProfile, project)); 1012 } 1013 1014 JCRProjectMember member = getOrCreateProjectMember(project, userIdentity); 1015 1016 project.setManager(userIdentity); 1017 1018 _profileAssignmentStorageExtensionPoint.allowProfileToUser(userIdentity, RightManager.READER_PROFILE_ID, project); 1019 _profileAssignmentStorageExtensionPoint.allowProfileToUser(userIdentity, profileId, project); 1020 _notifyAclUpdated(userIdentity, project, Arrays.asList(RightManager.READER_PROFILE_ID, profileId)); 1021 1022 // Allow the user to access the project's site 1023 _allowReadAccessOnProjectDashboard(member, project); 1024 1025 for (WorkspaceModule module : _projectManager.getModules(project)) 1026 { 1027 ModifiableResourceCollection moduleRootNode = module.getModuleRoot(project, false); 1028 _profileAssignmentStorageExtensionPoint.allowProfileToUser(userIdentity, RightManager.READER_PROFILE_ID, moduleRootNode); 1029 _profileAssignmentStorageExtensionPoint.allowProfileToUser(userIdentity, profileId, moduleRootNode); 1030 _notifyAclUpdated(userIdentity, moduleRootNode, Arrays.asList(RightManager.READER_PROFILE_ID, profileId)); 1031 1032 // Update read access on project module's pages 1033 AmetysObjectIterable<Page> modulePages = module.getModulePages(project, null); 1034 for (Page modulePage : modulePages) 1035 { 1036 _updateReadAccessOnModulePage(member, modulePage, true); 1037 } 1038 } 1039 1040 project.saveChanges(); 1041 } 1042 } 1043 1044 private void _removeManagerRights(Project project, UserIdentity userIdentity) 1045 { 1046 _profileAssignmentStorageExtensionPoint.getAllowedProfilesForUser(project, userIdentity).stream() 1047 .filter(((Predicate<String>) RightManager.READER_PROFILE_ID::equals).negate()) // keep reader profile for project access, only remove manager specific rights 1048 .forEach(profile -> _profileAssignmentStorageExtensionPoint.removeAllowedProfileFromUser(userIdentity, profile, project)); 1049 } 1050 1051 private void _notifyAclUpdated(UserIdentity userIdentity, AmetysObject aclContext, Collection<String> aclProfiles) 1052 { 1053 Map<String, Object> eventParams = new HashMap<>(); 1054 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT, aclContext); 1055 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, aclContext.getId()); 1056 eventParams.put(org.ametys.core.ObservationConstants.ARGS_ACL_PROFILES, aclProfiles); 1057 1058 _observationManager.notify(new Event(org.ametys.core.ObservationConstants.EVENT_ACL_UPDATED, userIdentity, eventParams)); 1059 } 1060 1061 /** 1062 * Retrieve or create a user in a project 1063 * @param project The project 1064 * @param userIdentity the user 1065 * @return The user 1066 */ 1067 public JCRProjectMember getOrCreateProjectMember(Project project, UserIdentity userIdentity) 1068 { 1069 Predicate<? super AmetysObject> findMemberPredicate = memberNode -> MemberType.USER.toString().equals(((JCRProjectMember) memberNode).getType()) 1070 && userIdentity.equals(((JCRProjectMember) memberNode).getUser()); 1071 JCRProjectMember projectMember = _getOrCreateProjectMember(project, findMemberPredicate); 1072 1073 if (projectMember.needsSave()) 1074 { 1075 projectMember.setUser(userIdentity); 1076 projectMember.setType(MemberType.USER); 1077 } 1078 1079 return projectMember; 1080 } 1081 1082 /** 1083 * Retrieve or create a group as a member in a project 1084 * @param project The project 1085 * @param groupIdentity the group 1086 * @return The user 1087 */ 1088 public JCRProjectMember getOrCreateProjectMember(Project project, GroupIdentity groupIdentity) 1089 { 1090 Predicate<? super AmetysObject> findMemberPredicate = memberNode -> MemberType.GROUP.toString().equals(((JCRProjectMember) memberNode).getType()) 1091 && groupIdentity.equals(((JCRProjectMember) memberNode).getGroup()); 1092 JCRProjectMember projectMember = _getOrCreateProjectMember(project, findMemberPredicate); 1093 1094 if (projectMember.needsSave()) 1095 { 1096 projectMember.setGroup(groupIdentity); 1097 projectMember.setType(MemberType.GROUP); 1098 } 1099 1100 return projectMember; 1101 } 1102 1103 /** 1104 * Retrieve or create a member in a project 1105 * @param project The project 1106 * @param findMemberPredicate The predicate to find the member node 1107 * @return The member node. A new node is created if the member node was not found 1108 */ 1109 protected JCRProjectMember _getOrCreateProjectMember(Project project, Predicate<? super AmetysObject> findMemberPredicate) 1110 { 1111 ModifiableTraversableAmetysObject membersNode = _getProjectMembersNode(project); 1112 1113 Optional<AmetysObject> member = _getProjectMembersNode(project).getChildren() 1114 .stream() 1115 .filter(memberNode -> memberNode instanceof JCRProjectMember) 1116 .filter(findMemberPredicate) 1117 .findFirst(); 1118 if (member.isPresent()) 1119 { 1120 return (JCRProjectMember) member.get(); 1121 } 1122 1123 String baseName = "member"; 1124 String name = baseName; 1125 int index = 1; 1126 while (membersNode.hasChild(name)) 1127 { 1128 index++; 1129 name = baseName + "-" + index; 1130 } 1131 1132 return membersNode.createChild(name, __PROJECT_MEMBER_NODE_TYPE); 1133 } 1134 1135 1136 /** 1137 * Remove a user from a project 1138 * @param projectName The project name 1139 * @param identity The identity of the user or group, who must be a member of the project 1140 * @param type The type of the member, user or group 1141 * @return The error code, if an error occurred 1142 * @throws IllegalAccessException If the user cannot execute this operation 1143 */ 1144 @Callable 1145 public Map<String, Object> removeMember(String projectName, String identity, String type) throws IllegalAccessException 1146 { 1147 Map<String, Object> result = new HashMap<>(); 1148 1149 boolean isTypeUser = JCRProjectMember.MemberType.USER.toString().equals(type); 1150 boolean isTypeGroup = JCRProjectMember.MemberType.GROUP.toString().equals(type); 1151 UserIdentity user = Optional.ofNullable(identity) 1152 .filter(id -> id != null && isTypeUser) 1153 .map(UserIdentity::stringToUserIdentity) 1154 .orElse(null); 1155 GroupIdentity group = Optional.ofNullable(identity) 1156 .filter(id -> id != null && isTypeGroup) 1157 .map(GroupIdentity::stringToGroupIdentity) 1158 .orElse(null); 1159 1160 if ((isTypeGroup && group == null) || (isTypeUser && user == null)) 1161 { 1162 result.put("message", isTypeGroup ? "unknow-group" : "unknow-user"); 1163 return result; 1164 } 1165 1166 Project project = _projectManager.getProject(projectName); 1167 if (project == null) 1168 { 1169 result.put("message", "unknow-project"); 1170 return result; 1171 } 1172 1173 if (!_projectRightHelper.hasRight(__RIGHTS_REMOVE_MEMBER_TO_PROJECT, project)) 1174 { 1175 throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to access a privilege feature without convenient right [" + __RIGHTS_REMOVE_MEMBER_TO_PROJECT + ", /projects" + project.getPath() + "]"); 1176 } 1177 1178 JCRProjectMember projectMember = null; 1179 if (isTypeUser) 1180 { 1181 projectMember = _getProjectMember(project, user); 1182 } 1183 else if (isTypeGroup) 1184 { 1185 projectMember = _getProjectMember(project, group); 1186 } 1187 1188 if (projectMember == null) 1189 { 1190 result.put("message", "unknow-member"); 1191 return result; 1192 } 1193 1194 _removeMemberProfiles(project, projectMember); 1195 1196 projectMember.remove(); 1197 project.saveChanges(); 1198 1199 Map<String, Object> eventParams = new HashMap<>(); 1200 eventParams.put(ObservationConstants.ARGS_MEMBER_IDENTITY, identity); 1201 eventParams.put(ObservationConstants.ARGS_MEMBER_IDENTITY_TYPE, type); 1202 eventParams.put(ObservationConstants.ARGS_PROJECT, project); 1203 _observationManager.notify(new Event(ObservationConstants.EVENT_MEMBER_DELETED, _currentUserProvider.getUser(), eventParams)); 1204 1205 return result; 1206 } 1207 1208 private JCRProjectMember _getProjectMember(Project project, GroupIdentity group) 1209 { 1210 JCRProjectMember projectMember = null; 1211 ModifiableTraversableAmetysObject membersNode = _getProjectMembersNode(project); 1212 1213 for (AmetysObject memberNode : membersNode.getChildren()) 1214 { 1215 if (memberNode instanceof JCRProjectMember) 1216 { 1217 JCRProjectMember member = (JCRProjectMember) memberNode; 1218 if (MemberType.GROUP.toString().equals(member.getType()) && group.equals(member.getGroup())) 1219 { 1220 projectMember = (JCRProjectMember) memberNode; 1221 } 1222 1223 } 1224 } 1225 return projectMember; 1226 } 1227 1228 private JCRProjectMember _getProjectMember(Project project, UserIdentity user) 1229 { 1230 JCRProjectMember projectMember = null; 1231 ModifiableTraversableAmetysObject membersNode = _getProjectMembersNode(project); 1232 1233 for (AmetysObject memberNode : membersNode.getChildren()) 1234 { 1235 if (memberNode instanceof JCRProjectMember) 1236 { 1237 JCRProjectMember member = (JCRProjectMember) memberNode; 1238 if (MemberType.USER.toString().equals(member.getType()) && user.equals(member.getUser())) 1239 { 1240 projectMember = (JCRProjectMember) memberNode; 1241 } 1242 } 1243 } 1244 return projectMember; 1245 } 1246 1247 private void _removeMemberProfiles(Project project, JCRProjectMember projectMember) 1248 { 1249 // Remove rights on project and project's site 1250 _removeReadAccessOnProjectDashboard(projectMember, project); 1251 1252 for (WorkspaceModule module : _projectManager.getModules(project)) 1253 { 1254 ModifiableResourceCollection moduleRootNode = module.getModuleRoot(project, false); 1255 // Remove profiles on module 1256 _removeMemberProfiles(projectMember, moduleRootNode); 1257 1258 // Remove profiles on module's pages 1259 AmetysObjectIterable<Page> modulePages = module.getModulePages(project, null); 1260 for (Page modulePage : modulePages) 1261 { 1262 _removeMemberProfiles(projectMember, modulePage); 1263 } 1264 } 1265 1266 // Remove rights on project 1267 _removeMemberProfiles(projectMember, project); 1268 } 1269 1270 /** 1271 * Retrieves the users node of the project 1272 * The users node will be created if necessary 1273 * @param project The project 1274 * @return The users node of the project 1275 */ 1276 protected ModifiableTraversableAmetysObject _getProjectMembersNode(Project project) 1277 { 1278 if (project == null) 1279 { 1280 throw new AmetysRepositoryException("Error getting the project users node, project is null"); 1281 } 1282 1283 try 1284 { 1285 ModifiableTraversableAmetysObject usersNode; 1286 if (project.hasChild(__PROJECT_MEMBERS_NODE)) 1287 { 1288 usersNode = project.getChild(__PROJECT_MEMBERS_NODE); 1289 } 1290 else 1291 { 1292 usersNode = project.createChild(__PROJECT_MEMBERS_NODE, __PROJECT_MEMBERS_NODE_TYPE); 1293 } 1294 1295 return usersNode; 1296 } 1297 catch (AmetysRepositoryException e) 1298 { 1299 throw new AmetysRepositoryException("Error getting the project users node", e); 1300 } 1301 } 1302 1303 /** 1304 * Get the JSON representation of a group 1305 * @param group The group 1306 * @return The group 1307 */ 1308 protected Map<String, Object> group2Json(Group group) 1309 { 1310 Map<String, Object> infos = new HashMap<>(); 1311 infos.put("id", group.getIdentity().getId()); 1312 infos.put("label", group.getLabel()); 1313 infos.put("sortablename", group.getLabel()); 1314 infos.put("groupDirectory", group.getIdentity().getDirectoryId()); 1315 return infos; 1316 } 1317}