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.core.ui.right; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028 029import org.ametys.core.ObservationConstants; 030import org.ametys.core.group.GroupDirectoryDAO; 031import org.ametys.core.group.GroupIdentity; 032import org.ametys.core.group.GroupManager; 033import org.ametys.core.observation.Event; 034import org.ametys.core.observation.ObservationManager; 035import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint; 036import org.ametys.core.right.RightAssignmentContext; 037import org.ametys.core.right.RightAssignmentContextExtensionPoint; 038import org.ametys.core.right.RightManager.RightResult; 039import org.ametys.core.right.RightsException; 040import org.ametys.core.ui.Callable; 041import org.ametys.core.ui.ClientSideElement; 042import org.ametys.core.ui.ClientSideElementHelper; 043import org.ametys.core.ui.StaticClientSideElement; 044import org.ametys.core.user.UserIdentity; 045import org.ametys.plugins.core.user.UserHelper; 046 047import com.google.common.collect.Sets; 048 049/** 050 * {@link ClientSideElement} for the tool displaying the profile assignments 051 */ 052public class ProfileAssignmentsToolClientSideElement extends StaticClientSideElement 053{ 054 /** The profile assignment storage component */ 055 protected ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP; 056 /** The extension point for right assignment contexts */ 057 protected RightAssignmentContextExtensionPoint _rightAssignmentContextEP; 058 /** The DAO for group directories */ 059 protected GroupDirectoryDAO _groupDirectoryDAO; 060 /** The group manager */ 061 protected GroupManager _groupManager; 062 /** The observation manager */ 063 protected ObservationManager _observationManager; 064 /** The user helper */ 065 protected UserHelper _userHelper; 066 067 /** 068 * Enumeration of all possible access types 069 */ 070 public enum AccessType 071 { 072 /** 073 * Indicates that the access is allowed 074 */ 075 ALLOW 076 { 077 @Override 078 public String toString() 079 { 080 return "allow"; 081 } 082 }, 083 /** 084 * Indicates that the access is denied 085 */ 086 DENY 087 { 088 @Override 089 public String toString() 090 { 091 return "deny"; 092 } 093 }, 094 /** 095 * Indicates that the access is allowed by inheritance 096 */ 097 INHERITED_ALLOW 098 { 099 @Override 100 public String toString() 101 { 102 return "inherited_allow"; 103 } 104 }, 105 /** 106 * Indicates that the access is denied by inheritance 107 */ 108 INHERITED_DENY 109 { 110 @Override 111 public String toString() 112 { 113 return "inherited_deny"; 114 } 115 }, 116 /** 117 * Indicates that the access can not be determined 118 */ 119 UNKNOWN 120 { 121 @Override 122 public String toString() 123 { 124 return "unknown"; 125 } 126 } 127 } 128 129 /** 130 * Enumeration of all possible target types 131 */ 132 public enum TargetType 133 { 134 /** 135 * Indicates that the target is the anonymous user 136 */ 137 ANONYMOUS 138 { 139 @Override 140 public String toString() 141 { 142 return "anonymous"; 143 } 144 }, 145 /** 146 * Indicates that the target is the anonymous user 147 */ 148 ANYCONNECTED_USER 149 { 150 @Override 151 public String toString() 152 { 153 return "anyconnected_user"; 154 } 155 }, 156 /** 157 * Indicates that the target is a user 158 */ 159 USER 160 { 161 @Override 162 public String toString() 163 { 164 return "user"; 165 } 166 }, 167 /** 168 * Indicates that the target is a group 169 */ 170 GROUP 171 { 172 @Override 173 public String toString() 174 { 175 return "group"; 176 } 177 } 178 } 179 180 @Override 181 public void service(ServiceManager smanager) throws ServiceException 182 { 183 super.service(smanager); 184 _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) smanager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE); 185 _rightAssignmentContextEP = (RightAssignmentContextExtensionPoint) smanager.lookup(RightAssignmentContextExtensionPoint.ROLE); 186 _groupDirectoryDAO = (GroupDirectoryDAO) smanager.lookup(GroupDirectoryDAO.ROLE); 187 _groupManager = (GroupManager) smanager.lookup(GroupManager.ROLE); 188 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 189 _userHelper = (UserHelper) smanager.lookup(UserHelper.ROLE); 190 } 191 192 @SuppressWarnings("unchecked") 193 @Override 194 public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters) 195 { 196 List<Script> scripts = super.getScripts(ignoreRights, contextParameters); 197 198 if (scripts.size() > 0) 199 { 200 Script script = ClientSideElementHelper.cloneScript(scripts.get(0)); 201 202 Map<String, Object> jsClasses = new HashMap<>(); 203 script.getParameters().put("classes", jsClasses); 204 205 boolean excludePrivate = true; 206 Set<String> rightContextIds = _rightAssignmentContextEP.getExtensionsIds(); 207 if (script.getParameters().containsKey("right-contexts")) 208 { 209 excludePrivate = false; 210 // Restrict the right contexts to the configured right contexts if exist 211 Object ctxConfig = ((Map<String, Object>) script.getParameters().get("right-contexts")).get("right-context"); 212 if (ctxConfig instanceof List) 213 { 214 rightContextIds = new HashSet<>((List<String>) ctxConfig); 215 } 216 else 217 { 218 rightContextIds = Sets.newHashSet((String) ctxConfig); 219 } 220 } 221 222 for (String rightContextId: rightContextIds) 223 { 224 RightAssignmentContext rightAssignmentContext = _rightAssignmentContextEP.getExtension(rightContextId); 225 226 if (!excludePrivate || !rightAssignmentContext.isPrivate()) 227 { 228 List<Script> rightAssignmentContextScripts = rightAssignmentContext.getScripts(ignoreRights, contextParameters); 229 int index = 0; 230 for (Script rightAssignmentContextScript: rightAssignmentContextScripts) 231 { 232 Map<String, Object> classInfo = new HashMap<>(); 233 classInfo.put("className", rightAssignmentContextScript.getScriptClassname()); 234 classInfo.put("serverId", rightContextId); 235 classInfo.put("parameters", rightAssignmentContextScript.getParameters()); 236 jsClasses.put(rightContextId + "-" + index++, classInfo); 237 238 script.getScriptFiles().addAll(rightAssignmentContextScript.getScriptFiles()); 239 script.getCSSFiles().addAll(rightAssignmentContextScript.getCSSFiles()); 240 } 241 } 242 243 } 244 245 scripts = new ArrayList<>(); 246 scripts.add(script); 247 } 248 249 return scripts; 250 } 251 252 /** 253 * Gets the groups of a user as JSON 254 * @param login The login of the user 255 * @param populationId The population of the user 256 * @return the groups of a user as JSON 257 */ 258 @Callable 259 public List<Map<String, Object>> getUserGroups(String login, String populationId) 260 { 261 return _groupManager.getUserGroups(new UserIdentity(login, populationId)).stream() 262 .map(this::_groupToJson) 263 .collect(Collectors.toList()); 264 } 265 266 private Map<String, Object> _groupToJson(GroupIdentity groupIdentity) 267 { 268 Map<String, Object> result = new HashMap<>(); 269 result.put("groupId", groupIdentity.getId()); 270 result.put("groupDirectory", groupIdentity.getDirectoryId()); 271 return result; 272 } 273 274 /** 275 * Save some changes made client-side. 276 * @param rightAssignmentCtxId The id of the right assignment context 277 * @param jsContext The JS object context 278 * @param assignmentsInfo The list of all the changes to make. Each map in the list must contain the following keys: 279 * <ol> 280 * <li><b>profileId</b> for the id of the profile (as a string)</li> 281 * <li><b>assignment</b> for the kind of assignment (can be ACCESS_TYPE_ALLOW, ACCESS_TYPE_DENY...)</li> 282 * <li><b>assignmentType</b> expects one of these four strings: "user", "group", "anonymous", "anyConnectedUser"</li> 283 * <li><b>identity</b> Can be null if assignmentType is "anonymous" or "anyConnectedUser". If "user", must be a map with the keys "login" and "populationId". If "group", must be a map with the keys "groupId" and "groupDirectory"</li> 284 * </ol> 285 */ 286 @SuppressWarnings("unchecked") 287 @Callable 288 public void saveChanges(String rightAssignmentCtxId, Object jsContext, List<Map<String, Object>> assignmentsInfo) 289 { 290 if (_rightManager.hasRight(_currentUserProvider.getUser(), "Runtime_Rights_Rights_Handle", "/${WorkspaceName}") != RightResult.RIGHT_ALLOW) 291 { 292 throw new RightsException("The user '" + _currentUserProvider.getUser() + "' try to assign profile without sufficient rights"); 293 } 294 295 Set<String> updatedProfiles = new HashSet<>(); 296 RightAssignmentContext rightAssignmentContext = _rightAssignmentContextEP.getExtension(rightAssignmentCtxId); 297 Object context = rightAssignmentContext.convertJSContext(jsContext); 298 String contextIdentifier = rightAssignmentContext.getContextIdentifier(context); 299 for (Map<String, Object> assignmentInfo : assignmentsInfo) 300 { 301 String profileId = (String) assignmentInfo.get("profileId"); 302 updatedProfiles.add(profileId); 303 String assignment = (String) assignmentInfo.get("assignment"); 304 String targetType = (String) assignmentInfo.get("targetType"); 305 Map<String, String> identity = (Map<String, String>) assignmentInfo.get("identity"); 306 _saveChange(context, profileId, assignment, targetType, identity); 307 } 308 _notifyObservers(context, contextIdentifier, updatedProfiles); 309 } 310 311 private void _notifyObservers(Object context, String contextIdentifier, Set<String> profileIds) 312 { 313 Map<String, Object> eventParams = new HashMap<>(); 314 eventParams.put(ObservationConstants.ARGS_ACL_CONTEXT, context); 315 eventParams.put(ObservationConstants.ARGS_ACL_CONTEXT_IDENTIFIER, contextIdentifier); 316 eventParams.put(ObservationConstants.ARGS_ACL_PROFILES, profileIds); 317 318 _observationManager.notify(new Event(ObservationConstants.EVENT_ACL_UPDATED, _currentUserProvider.getUser(), eventParams)); 319 } 320 321 /** 322 * Get the first permission given by inheritance for a object context and profiles 323 * @param rightAssignmentCtxId The id of the right assignment context 324 * @param jsContext The JS object context 325 * @param profileIds The list of profiles 326 * @param targetType The type of target : anonymous, any connected users, a user or a group 327 * @param identity The identity of the target. Can be null if the target is anonymous or any connected users 328 * @return The first access type given by inheritance for each profile 329 */ 330 @Callable 331 public Map<String, String> getInheritedAssignments (String rightAssignmentCtxId, Object jsContext, List<String> profileIds, String targetType, Map<String, String> identity) 332 { 333 Map<String, String> assignments = new HashMap<>(); 334 335 for (String profileId : profileIds) 336 { 337 assignments.put(profileId, getInheritedAssignment(rightAssignmentCtxId, jsContext, profileId, targetType, identity)); 338 } 339 return assignments; 340 } 341 342 /** 343 * Get the first permission given by inheritance for a object context and a specific profile 344 * @param rightAssignmentCtxId The id of the right assignment context 345 * @param jsContext The JS object context 346 * @param profileId The id of profile 347 * @param targetType The type of target : anonymous, any connected users, a user or a group 348 * @param identity The identity of the target. Can be null if the target is anonymous or any connected users 349 * @return The first access type given by inheritance 350 */ 351 @Callable 352 public String getInheritedAssignment (String rightAssignmentCtxId, Object jsContext, String profileId, String targetType, Map<String, String> identity) 353 { 354 RightAssignmentContext rightCtx = _rightAssignmentContextEP.getExtension(rightAssignmentCtxId); 355 Object context = rightCtx.convertJSContext(jsContext); 356 357 switch (TargetType.valueOf(targetType.toUpperCase())) 358 { 359 case ANONYMOUS: 360 return _getInheritedAssignmentForAnonymous(rightCtx, context, profileId); 361 case ANYCONNECTED_USER: 362 return _getInheritedAssignmentForAnyconnected(rightCtx, context, profileId); 363 case USER: 364 UserIdentity user = _userHelper.json2userIdentity(identity); 365 return _getInheritedAssignmentForUser(rightCtx, context, profileId, user); 366 case GROUP: 367 GroupIdentity group = new GroupIdentity(identity.get("groupId"), identity.get("groupDirectory")); 368 return _getInheritedAssignmentForGroup(rightCtx, context, profileId, group); 369 default: 370 return AccessType.UNKNOWN.toString(); 371 } 372 } 373 374 private String _getInheritedAssignmentForAnonymous (RightAssignmentContext extension, Object context, String profileId) 375 { 376 String value = AccessType.UNKNOWN.toString(); 377 378 Set<Object> parentContexts = extension.getParentContexts(context); 379 if (parentContexts != null) 380 { 381 for (Object parentContext : parentContexts) 382 { 383 Set<String> deniedProfiles = _profileAssignmentStorageEP.getDeniedProfilesForAnonymous(parentContext); 384 if (deniedProfiles.contains(profileId)) 385 { 386 return AccessType.INHERITED_DENY.toString(); 387 } 388 389 Set<String> allowedProfiles = _profileAssignmentStorageEP.getAllowedProfilesForAnonymous(parentContext); 390 if (allowedProfiles.contains(profileId)) 391 { 392 value = AccessType.INHERITED_ALLOW.toString(); 393 } 394 395 String parentsValue = _getInheritedAssignmentForAnonymous(extension, parentContext, profileId); 396 if (!AccessType.UNKNOWN.toString().equals(parentsValue)) 397 { 398 value = parentsValue; 399 } 400 } 401 } 402 403 return value; 404 } 405 406 private String _getInheritedAssignmentForAnyconnected(RightAssignmentContext extension, Object context, String profileId) 407 { 408 String value = AccessType.UNKNOWN.toString(); 409 410 Set<Object> parentContexts = extension.getParentContexts(context); 411 if (parentContexts != null) 412 { 413 for (Object parentContext : parentContexts) 414 { 415 Set<String> deniedProfiles = _profileAssignmentStorageEP.getDeniedProfilesForAnyConnectedUser(parentContext); 416 if (deniedProfiles.contains(profileId)) 417 { 418 return AccessType.INHERITED_DENY.toString(); 419 } 420 421 Set<String> allowedProfiles = _profileAssignmentStorageEP.getAllowedProfilesForAnyConnectedUser(parentContext); 422 if (allowedProfiles.contains(profileId)) 423 { 424 value = AccessType.INHERITED_ALLOW.toString(); 425 } 426 427 String parentsValue = _getInheritedAssignmentForAnyconnected(extension, parentContext, profileId); 428 if (!AccessType.UNKNOWN.toString().equals(parentsValue)) 429 { 430 value = parentsValue; 431 } 432 } 433 } 434 435 return value; 436 } 437 438 private String _getInheritedAssignmentForUser(RightAssignmentContext extension, Object context, String profileId, UserIdentity user) 439 { 440 String value = AccessType.UNKNOWN.toString(); 441 442 Set<Object> parentContexts = extension.getParentContexts(context); 443 if (parentContexts != null) 444 { 445 for (Object parentContext : parentContexts) 446 { 447 // FIXME Optimization _profileAssignmentStorageEP.getDeniedProfilesForUser(parentContext, user) 448 Map<UserIdentity, Set<String>> deniedProfiles = _profileAssignmentStorageEP.getDeniedProfilesForUsers(parentContext); 449 if (deniedProfiles.containsKey(user) && deniedProfiles.get(user).contains(profileId)) 450 { 451 return AccessType.INHERITED_DENY.toString(); 452 } 453 454 Map<UserIdentity, Set<String>> allowedProfiles = _profileAssignmentStorageEP.getAllowedProfilesForUsers(parentContext); 455 if (allowedProfiles.containsKey(user) && allowedProfiles.get(user).contains(profileId)) 456 { 457 value = AccessType.INHERITED_ALLOW.toString(); 458 } 459 460 String parentsValue = _getInheritedAssignmentForUser(extension, parentContext, profileId, user); 461 if (!AccessType.UNKNOWN.toString().equals(parentsValue)) 462 { 463 value = parentsValue; 464 } 465 } 466 } 467 468 return value; 469 } 470 471 private String _getInheritedAssignmentForGroup(RightAssignmentContext extension, Object context, String profileId, GroupIdentity group) 472 { 473 String value = AccessType.UNKNOWN.toString(); 474 475 Set<Object> parentContexts = extension.getParentContexts(context); 476 if (parentContexts != null) 477 { 478 for (Object parentContext : parentContexts) 479 { 480 // FIXME Optimization _profileAssignmentStorageEP.getDeniedProfilesForUser(parentContext, user) 481 Map<GroupIdentity, Set<String>> deniedProfiles = _profileAssignmentStorageEP.getDeniedProfilesForGroups(parentContext); 482 if (deniedProfiles.containsKey(group) && deniedProfiles.get(group).contains(profileId)) 483 { 484 return AccessType.INHERITED_DENY.toString(); 485 } 486 487 Map<GroupIdentity, Set<String>> allowedProfiles = _profileAssignmentStorageEP.getAllowedProfilesForGroups(parentContext); 488 if (allowedProfiles.containsKey(group) && allowedProfiles.get(group).contains(profileId)) 489 { 490 value = AccessType.INHERITED_ALLOW.toString(); 491 } 492 493 String parentsValue = _getInheritedAssignmentForGroup(extension, parentContext, profileId, group); 494 if (!AccessType.UNKNOWN.toString().equals(parentsValue)) 495 { 496 value = parentsValue; 497 } 498 } 499 } 500 501 return value; 502 } 503 504 private void _saveChange(Object context, String profileId, String assignment, String targetType, Map<String, String> identity) 505 { 506 AccessType accessType = assignment != null ? AccessType.valueOf(assignment.toUpperCase()) : AccessType.UNKNOWN; 507 switch (TargetType.valueOf(targetType.toUpperCase())) 508 { 509 case ANONYMOUS: 510 switch (accessType) 511 { 512 case ALLOW: 513 _profileAssignmentStorageEP.removeDeniedProfileFromAnonymous(profileId, context); 514 _profileAssignmentStorageEP.allowProfileToAnonymous(profileId, context); 515 break; 516 case DENY: 517 _profileAssignmentStorageEP.removeAllowedProfileFromAnonymous(profileId, context); 518 _profileAssignmentStorageEP.denyProfileToAnonymous(profileId, context); 519 break; 520 default: 521 _profileAssignmentStorageEP.removeAllowedProfileFromAnonymous(profileId, context); 522 _profileAssignmentStorageEP.removeDeniedProfileFromAnonymous(profileId, context); 523 break; 524 } 525 break; 526 527 case ANYCONNECTED_USER: 528 switch (accessType) 529 { 530 case ALLOW: 531 _profileAssignmentStorageEP.removeDeniedProfileFromAnyConnectedUser(profileId, context); 532 _profileAssignmentStorageEP.allowProfileToAnyConnectedUser(profileId, context); 533 break; 534 case DENY: 535 _profileAssignmentStorageEP.removeAllowedProfileFromAnyConnectedUser(profileId, context); 536 _profileAssignmentStorageEP.denyProfileToAnyConnectedUser(profileId, context); 537 break; 538 default: 539 _profileAssignmentStorageEP.removeAllowedProfileFromAnyConnectedUser(profileId, context); 540 _profileAssignmentStorageEP.removeDeniedProfileFromAnyConnectedUser(profileId, context); 541 break; 542 } 543 break; 544 545 case USER: 546 UserIdentity user = _userHelper.json2userIdentity(identity); 547 switch (accessType) 548 { 549 case ALLOW: 550 _profileAssignmentStorageEP.removeDeniedProfileFromUser(user, profileId, context); 551 _profileAssignmentStorageEP.allowProfileToUser(user, profileId, context); 552 break; 553 case DENY: 554 _profileAssignmentStorageEP.removeAllowedProfileFromUser(user, profileId, context); 555 _profileAssignmentStorageEP.denyProfileToUser(user, profileId, context); 556 break; 557 default: 558 _profileAssignmentStorageEP.removeAllowedProfileFromUser(user, profileId, context); 559 _profileAssignmentStorageEP.removeDeniedProfileFromUser(user, profileId, context); 560 break; 561 } 562 break; 563 564 case GROUP: 565 GroupIdentity group = new GroupIdentity(identity.get("groupId"), identity.get("groupDirectory")); 566 switch (accessType) 567 { 568 case ALLOW: 569 _profileAssignmentStorageEP.removeDeniedProfileFromGroup(group, profileId, context); 570 _profileAssignmentStorageEP.allowProfileToGroup(group, profileId, context); 571 break; 572 case DENY: 573 _profileAssignmentStorageEP.removeAllowedProfileFromGroup(group, profileId, context); 574 _profileAssignmentStorageEP.denyProfileToGroup(group, profileId, context); 575 break; 576 default: 577 _profileAssignmentStorageEP.removeAllowedProfileFromGroup(group, profileId, context); 578 _profileAssignmentStorageEP.removeDeniedProfileFromGroup(group, profileId, context); 579 break; 580 } 581 break; 582 default: 583 break; 584 } 585 } 586 587 588}