001/* 002 * Copyright 2020 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.repository.jcr; 017 018import java.util.Collections; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Map; 022import java.util.Set; 023import java.util.stream.Collectors; 024 025import javax.jcr.Node; 026import javax.jcr.NodeIterator; 027import javax.jcr.PathNotFoundException; 028import javax.jcr.Repository; 029import javax.jcr.RepositoryException; 030import javax.jcr.Session; 031import javax.jcr.Value; 032import javax.jcr.lock.Lock; 033import javax.jcr.lock.LockManager; 034import javax.jcr.query.Query; 035 036import org.apache.avalon.framework.component.Component; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.jackrabbit.util.ISO9075; 041import org.apache.jackrabbit.util.Text; 042import org.slf4j.Logger; 043 044import org.ametys.core.group.GroupIdentity; 045import org.ametys.core.right.ProfileAssignmentStorage.AnonymousOrAnyConnectedKeys; 046import org.ametys.core.right.ProfileAssignmentStorage.UserOrGroup; 047import org.ametys.core.user.UserIdentity; 048import org.ametys.core.util.LambdaUtils; 049import org.ametys.plugins.repository.ACLAmetysObject; 050import org.ametys.plugins.repository.AmetysObjectResolver; 051import org.ametys.plugins.repository.AmetysRepositoryException; 052import org.ametys.plugins.repository.ModifiableACLAmetysObject; 053import org.ametys.plugins.repository.ModifiableACLAmetysObjectProfileAssignmentStorage; 054import org.ametys.plugins.repository.RepositoryConstants; 055import org.ametys.plugins.repository.provider.AbstractRepository; 056import org.ametys.plugins.repository.query.expression.Expression; 057import org.ametys.plugins.repository.query.expression.OrExpression; 058import org.ametys.runtime.plugin.component.LogEnabled; 059 060/** 061 * Helper for implementing {@link ModifiableACLAmetysObject} in JCR under its node. 062 */ 063public class ACLJCRAmetysObjectHelper implements Component, Serviceable, LogEnabled 064{ 065 /** The AmetysObject resolver */ 066 protected static AmetysObjectResolver _resolver; 067 /** The repository */ 068 protected static Repository _repository; 069 070 private static final String __NODE_NAME_ROOT_ACL = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":acl"; 071 private static final String __NODETYPE_ROOT_ACL = RepositoryConstants.NAMESPACE_PREFIX + ":acl"; 072 073 private static final String __NODE_NAME_ACL_USERS = "users"; 074 private static final String __NODE_NAME_ACL_GROUPS = "groups"; 075 private static final String __NODETYPE_ACL_USER = RepositoryConstants.NAMESPACE_PREFIX + ":acl-user"; 076 private static final String __NODETYPE_ACL_GROUP = RepositoryConstants.NAMESPACE_PREFIX + ":acl-group"; 077 private static final String __NODETYPE_UNSTRUCTURED = "nt:unstructured"; 078 079 private static final String __PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES = RepositoryConstants.NAMESPACE_PREFIX + ":allowed-any-connected-profiles"; 080 private static final String __PROPERTY_NAME_DENIED_ANY_CONNECTED_PROFILES = RepositoryConstants.NAMESPACE_PREFIX + ":denied-any-connected-profiles"; 081 private static final String __PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES = RepositoryConstants.NAMESPACE_PREFIX + ":allowed-anonymous-profiles"; 082 private static final String __PROPERTY_NAME_DENIED_ANONYMOUS_PROFILES = RepositoryConstants.NAMESPACE_PREFIX + ":denied-anonymous-profiles"; 083 084 private static final String __PROPERTY_NAME_ALLOWED_PROFILES = RepositoryConstants.NAMESPACE_PREFIX + ":allowed-profiles"; 085 private static final String __PROPERTY_NAME_DENIED_PROFILES = RepositoryConstants.NAMESPACE_PREFIX + ":denied-profiles"; 086 087 private static final String __PROPERTY_NAME_DISALLOW_INHERITANCE = RepositoryConstants.NAMESPACE_PREFIX + ":disallow-inheritance"; 088 089 private static final Map<AnonymousOrAnyConnectedKeys, Set<String>> __ANONYMOUS_OR_ANYCONNECTEDUSER_NORIGHT = Map.of( 090 AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED, Set.of(), 091 AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED, Set.of(), 092 AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED, Set.of(), 093 AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED, Set.of()); 094 private static final Map<UserOrGroup, Set<String>> __USER_OR_GROUP_NORIGHT = Map.of( 095 UserOrGroup.ALLOWED, Set.of(), 096 UserOrGroup.DENIED, Set.of()); 097 098 099 private static Logger _logger; 100 101 @Override 102 public void service(ServiceManager manager) throws ServiceException 103 { 104 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 105 _repository = (Repository) manager.lookup(AbstractRepository.ROLE); 106 } 107 108 public void setLogger(Logger logger) 109 { 110 _logger = logger; 111 } 112 113 114 /* -------------- */ 115 /* HAS PERMISSION */ 116 /* -------------- */ 117 118 private static Set<String> _convertNodeToPath(Set<? extends Object> rootNodes) 119 { 120 return rootNodes.stream().filter(JCRAmetysObject.class::isInstance).map(JCRAmetysObject.class::cast).map(LambdaUtils.wrap(ao -> ISO9075.encodePath(ao.getNode().getPath()))).collect(Collectors.toSet()); 121 } 122 123 124 /** 125 * Returns some profiles that are matching if any ACL Ametys object has one of the given profiles as allowed for the user 126 * @param user The user 127 * @param profileIds The ids of the profiles to check 128 * @param rootNodes The JCR root nodes where starts the query search (must be something like "//element(myNode, ametys:collection)"), it will be the beginning of the JCR query. Can be null to not restrict the search. 129 * @return If the Set is empty, it means the user has no matching profile.<br> 130 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for the user AND it can contains some other profiles that were not in the given profiles 131 */ 132 public static Set<String> hasUserAnyAllowedProfile(Set<? extends Object> rootNodes, UserIdentity user, Set<String> profileIds) 133 { 134 Expression expr = new AllowedProfileExpression(profileIds.toArray(new String[profileIds.size()])); 135 for (String rootPath : _convertNodeToPath(rootNodes)) 136 { 137 NodeIterator nodes = getACLUsers(user, rootPath, expr); 138 139 if (nodes.hasNext()) 140 { 141 // To be complete we could loop on all results, but we only want to answer the question and return additional data if we can 142 Node userNode = nodes.nextNode(); 143 return _getProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES); 144 } 145 } 146 return Set.of(); 147 } 148 149 /** 150 * Returns some profiles that are matching if any ACL Ametys object has one of the given profiles as allowed for the group 151 * @param groups The groups 152 * @param profileIds The ids of the profiles 153 * @param rootNodes The JCR root nodes where starts the query search (must be something like "//element(myNode, ametys:collection)"), it will be the beginning of the JCR query. Can be null to not restrict the search. 154 * @return If the Set is empty, it means the group has no matching profile.<br> 155 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for the group AND it can contains some other profiles that were not in the given profiles 156 */ 157 public static Set<String> hasGroupAnyAllowedProfile(Set<? extends Object> rootNodes, Set<GroupIdentity> groups, Set<String> profileIds) 158 { 159 if (!groups.isEmpty()) 160 { 161 Expression expr = new AllowedProfileExpression(profileIds.toArray(new String[profileIds.size()])); 162 for (String rootPath : _convertNodeToPath(rootNodes)) 163 { 164 // Approximative query (to be fast) 165 NodeIterator nodes = _getApprochingACLGroups(groups, rootPath, expr); 166 167 while (nodes.hasNext()) 168 { 169 Node groupNode = nodes.nextNode(); 170 171 // As the query was a fast approximative request, we now check if the result is fine 172 String groupId; 173 String directoryId; 174 try 175 { 176 groupId = Text.unescapeIllegalJcrChars(groupNode.getName()); 177 directoryId = groupNode.getParent().getName(); 178 } 179 catch (RepositoryException ex) 180 { 181 throw new AmetysRepositoryException("An error occured getting group information", ex); 182 } 183 184 if (groups.contains(new GroupIdentity(groupId, directoryId))) 185 { 186 // To be complete we could loop on all results, but we only want to answer the question and return additional data if we can 187 return _getProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES); 188 } 189 } 190 } 191 } 192 193 return Set.of(); 194 } 195 196 /** 197 * Returns some profiles that are matching if any ACL Ametys object has one of the given profiles as allowed for any connected user 198 * @param profileIds The ids of the profiles 199 * @param rootNodes The JCR root nodes where starts the query search (must be something like "//element(myNode, ametys:collection)"), it will be the beginning of the JCR query. Can be null to not restrict the search. 200 * @return If the Set is empty, it means any connected user has no matching profile.<br> 201 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for anyconnected user AND it can contains some other profiles that were not in the given profiles 202 */ 203 public static Set<String> hasAnyConnectedAnyAllowedProfile(Set<? extends Object> rootNodes, Set<String> profileIds) 204 { 205 Expression expr = new AnyConnectedAllowedProfileExpression(profileIds.toArray(new String[profileIds.size()])); 206 for (String rootPath : _convertNodeToPath(rootNodes)) 207 { 208 NodeIterator nodes = getACLRoots(rootPath, expr); 209 210 if (nodes.hasNext()) 211 { 212 // To be complete we could loop on all results, but we only want to answer the question and return additional data if we can 213 Node aclNode = nodes.nextNode(); 214 return _getProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES); 215 } 216 } 217 return Set.of(); 218 } 219 220 221 /** 222 * Returns some profiles that are matching if any ACL Ametys object has one of the given profiles as allowed for anonymous 223 * @param profileIds The ids of the profiles 224 * @param rootNodes The JCR root nodes where starts the query search (must be something like "//element(myNode, ametys:collection)"), it will be the beginning of the JCR query. Can be null to not restrict the search. 225 * @return If the Set is empty, it means anonymous has no matching profile.<br> 226 * If the Set is non empty, it contains at least one of the given profile BUT it may not contains all the matching profiles for anonymous AND it can contains some other profiles that were not in the given profiles 227 */ 228 public static Set<String> hasAnonymousAnyAllowedProfile(Set<? extends Object> rootNodes, Set<String> profileIds) 229 { 230 Expression expr = new AnonymousAllowedProfileExpression(profileIds.toArray(new String[profileIds.size()])); 231 for (String rootPath : _convertNodeToPath(rootNodes)) 232 { 233 NodeIterator nodes = getACLRoots(rootPath, expr); 234 235 if (nodes.hasNext()) 236 { 237 // To be complete we could loop on all results, but we only want to answer the question and return additional data if we can 238 Node aclNode = nodes.nextNode(); 239 return _getProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES); 240 } 241 } 242 return Set.of(); 243 } 244 245 /** 246 * Returns all ACL root objects (ametys:acl nodes) 247 * @param rootPath The root path to restrict the search. Can be null. 248 * @param predicat The predicat expression. Can be null. 249 * @return The ACL root objects 250 */ 251 public static NodeIterator getACLRoots (String rootPath, Expression predicat) 252 { 253 StringBuilder sb = new StringBuilder("/jcr:root"); 254 255 if (rootPath != null) 256 { 257 sb.append(rootPath); 258 } 259 260 sb.append("//element(*, ").append(__NODETYPE_ROOT_ACL).append(")"); 261 262 if (predicat != null) 263 { 264 sb.append("[").append(predicat.build()).append("]"); 265 } 266 267 return _query(sb.toString()); 268 } 269 270 /** 271 * Returns all ACL objects for a given user (ametys:acl-user nodes) 272 * @param user The user 273 * @param rootPath The root path to restrict the search. Can be null. 274 * @param predicat The predicat expression. Can be null. 275 * @return The ACL user objects for user 276 */ 277 public static NodeIterator getACLUsers (UserIdentity user, String rootPath, Expression predicat) 278 { 279 StringBuilder sb = new StringBuilder("/jcr:root"); 280 281 if (rootPath != null) 282 { 283 sb.append(rootPath); 284 } 285 286 sb.append("//element(*, ").append(__NODETYPE_ROOT_ACL).append(")") 287 .append("/").append(__NODE_NAME_ACL_USERS) 288 .append("/").append(user.getPopulationId()) 289 .append("/").append(ISO9075.encode(user.getLogin())); 290 291 if (predicat != null) 292 { 293 sb.append("[").append(predicat.build()).append("]"); 294 } 295 296 String jcrQuery = sb.toString(); 297 return _query(jcrQuery); 298 } 299 300 /** 301 * Returns all ACL objects for users (ametys:acl-user nodes) 302 * @param predicat The predicat expression. Can be null. 303 * @return The ACL user objects for users 304 */ 305 public static NodeIterator getACLUsers (Expression predicat) 306 { 307 StringBuilder sb = new StringBuilder(); 308 309 sb.append("//element(*, ").append(__NODETYPE_ACL_USER).append(")"); 310 311 if (predicat != null) 312 { 313 sb.append("[").append(predicat.build()).append("]"); 314 } 315 316 return _query(sb.toString()); 317 } 318 319 /** 320 * Returns all ACL objects for groups (ametys:acl-group nodes) 321 * @param predicat The predicat expression. Can be null. 322 * @return The ACL group objects for groups 323 */ 324 public static NodeIterator getACLGroups (Expression predicat) 325 { 326 StringBuilder sb = new StringBuilder(); 327 328 sb.append("//element(*, ").append(__NODETYPE_ACL_GROUP).append(")"); 329 330 if (predicat != null) 331 { 332 sb.append("[").append(predicat.build()).append("]"); 333 } 334 335 return _query(sb.toString()); 336 } 337 338 private static NodeIterator _getApprochingACLGroups (Set<GroupIdentity> groups, String rootPath, Expression predicat) 339 { 340 StringBuilder sb = new StringBuilder("/jcr:root"); 341 342 if (rootPath != null) 343 { 344 sb.append(rootPath); 345 } 346 347 sb.append("//element(*, ametys:acl-group)[("); 348 349 sb.append(groups.stream() 350 .map(GroupIdentity::getId) 351 .map(Text::escapeIllegalJcrChars) 352 .map(nodeName -> "fn:name()='" + nodeName + "'") 353 .collect(Collectors.joining(" or "))); 354 sb.append(")"); 355 356 if (predicat != null) 357 { 358 sb.append(" and ").append(predicat.build()); 359 } 360 361 sb.append("]"); 362 363 return _query(sb.toString()); 364 } 365 366 /** 367 * Returns all ACL objects for a given group (ametys:acl-group nodes) 368 * @param group The group 369 * @param rootPath The root path to restrict the search. Can be null. 370 * @param predicat The predicat expression. Can be null. 371 * @return The ACL user objects for groups 372 */ 373 public static NodeIterator getACLGroups (GroupIdentity group, String rootPath, Expression predicat) 374 { 375 StringBuilder sb = new StringBuilder("/jcr:root"); 376 377 if (rootPath != null) 378 { 379 sb.append(rootPath); 380 } 381 382 sb.append("//element(*, ").append(__NODETYPE_ROOT_ACL).append(")") 383 .append("/").append(__NODE_NAME_ACL_GROUPS) 384 .append("/").append(group.getDirectoryId()) 385 .append("/").append(ISO9075.encode(Text.escapeIllegalJcrChars(group.getId()))); 386 387 if (predicat != null) 388 { 389 sb.append("[").append(predicat.build()).append("]"); 390 } 391 392 return _query(sb.toString()); 393 } 394 395 private static NodeIterator _query (String jcrQuery) 396 { 397 Session session = null; 398 try 399 { 400 session = _repository.login(); 401 long t1 = System.currentTimeMillis(); 402 @SuppressWarnings("deprecation") 403 Query query = session.getWorkspace().getQueryManager().createQuery(jcrQuery, Query.XPATH); 404 if (_logger.isInfoEnabled()) 405 { 406 _logger.info("ACLJCR query '" + jcrQuery + "' executed in " + (System.currentTimeMillis() - t1) + " ms"); 407 } 408 return query.execute().getNodes(); 409 } 410 catch (RepositoryException ex) 411 { 412 if (session != null) 413 { 414 session.logout(); 415 } 416 throw new AmetysRepositoryException("An error occured executing the JCR query : " + jcrQuery, ex); 417 } 418 } 419 420 /* ------------------------------------------- */ 421 /* PROFILES FOR ANY CONNECTED USER / ANONYMOUS */ 422 /* ------------------------------------------- */ 423 424 /** 425 * Helper for {@link ACLAmetysObject#getProfilesForAnonymousAndAnyConnectedUser} 426 * @param node The JCR node for the Ametys object 427 * @return a map containing allowed/denied profiles that anonymous and any connected user has on the given object 428 */ 429 public static Map<AnonymousOrAnyConnectedKeys, Set<String>> getProfilesForAnonymousAndAnyConnectedUser(Node node) 430 { 431 Node aclNode = _getACLNode(node); 432 if (aclNode == null) 433 { 434 return __ANONYMOUS_OR_ANYCONNECTEDUSER_NORIGHT; 435 } 436 else 437 { 438 return Map.of(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED, _getProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES), 439 AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED, _getProperty(aclNode, __PROPERTY_NAME_DENIED_ANONYMOUS_PROFILES), 440 AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED, _getProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES), 441 AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED, _getProperty(aclNode, __PROPERTY_NAME_DENIED_ANY_CONNECTED_PROFILES)); 442 } 443 } 444 445 /** 446 * Helper for {@link ModifiableACLAmetysObject#addAllowedProfilesForAnyConnectedUser(Set)} 447 * @param node The JCR node for the Ametys object 448 * @param profileIds The profiles to add 449 */ 450 public static void addAllowedProfilesForAnyConnectedUser(Node node, Set<String> profileIds) 451 { 452 Node aclNode = _getOrCreateACLNode(node); 453 for (String profile : profileIds) 454 { 455 _addProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES, profile); 456 } 457 _save(node); 458 } 459 460 /** 461 * Helper for {@link ModifiableACLAmetysObject#removeAllowedProfilesForAnyConnectedUser(Set)} 462 * @param node The JCR node for the Ametys object 463 * @param profileIds The profiles to remove 464 */ 465 public static void removeAllowedProfilesForAnyConnectedUser(Node node, Set<String> profileIds) 466 { 467 Node aclNode = _getOrCreateACLNode(node); 468 for (String profile : profileIds) 469 { 470 _removeProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES, profile); 471 } 472 _save(node); 473 } 474 475 /** 476 * Helper for {@link ModifiableACLAmetysObject#addDeniedProfilesForAnyConnectedUser(Set)} 477 * @param node The JCR node for the Ametys object 478 * @param profileIds The profiles to add 479 */ 480 public static void addDeniedProfilesForAnyConnectedUser(Node node, Set<String> profileIds) 481 { 482 Node aclNode = _getOrCreateACLNode(node); 483 for (String profile : profileIds) 484 { 485 _addProperty(aclNode, __PROPERTY_NAME_DENIED_ANY_CONNECTED_PROFILES, profile); 486 } 487 _save(node); 488 } 489 490 /** 491 * Helper for {@link ModifiableACLAmetysObject#removeDeniedProfilesForAnyConnectedUser(Set)} 492 * @param node The JCR node for the Ametys object 493 * @param profileIds The profiles to remove 494 */ 495 public static void removeDeniedProfilesForAnyConnectedUser(Node node, Set<String> profileIds) 496 { 497 Node aclNode = _getOrCreateACLNode(node); 498 for (String profile : profileIds) 499 { 500 _removeProperty(aclNode, __PROPERTY_NAME_DENIED_ANY_CONNECTED_PROFILES, profile); 501 } 502 _save(node); 503 } 504 505 /** 506 * Helper for {@link ModifiableACLAmetysObject#addAllowedProfilesForAnonymous(Set)} 507 * @param node The JCR node for the Ametys object 508 * @param profileIds The profiles to add 509 */ 510 public static void addAllowedProfilesForAnonymous(Node node, Set<String> profileIds) 511 { 512 Node aclNode = _getOrCreateACLNode(node); 513 for (String profile : profileIds) 514 { 515 _addProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES, profile); 516 } 517 _save(node); 518 } 519 520 /** 521 * Helper for {@link ModifiableACLAmetysObject#removeAllowedProfilesForAnonymous(Set)} 522 * @param node The JCR node for the Ametys object 523 * @param profileIds The profiles to remove 524 */ 525 public static void removeAllowedProfilesForAnonymous(Node node, Set<String> profileIds) 526 { 527 Node aclNode = _getOrCreateACLNode(node); 528 for (String profile : profileIds) 529 { 530 _removeProperty(aclNode, __PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES, profile); 531 } 532 _save(node); 533 } 534 535 /** 536 * Helper for {@link ModifiableACLAmetysObject#addDeniedProfilesForAnyConnectedUser(Set)} 537 * @param node The JCR node for the Ametys object 538 * @param profileIds The profiles to add 539 */ 540 public static void addDeniedProfilesForAnonymous(Node node, Set<String> profileIds) 541 { 542 Node aclNode = _getOrCreateACLNode(node); 543 for (String profile : profileIds) 544 { 545 _addProperty(aclNode, __PROPERTY_NAME_DENIED_ANONYMOUS_PROFILES, profile); 546 } 547 _save(node); 548 } 549 550 /** 551 * Helper for {@link ModifiableACLAmetysObject#removeDeniedProfilesForAnyConnectedUser(Set)} 552 * @param node The JCR node for the Ametys object 553 * @param profileIds The profiles to remove 554 */ 555 public static void removeDeniedProfilesForAnonymous(Node node, Set<String> profileIds) 556 { 557 Node aclNode = _getOrCreateACLNode(node); 558 for (String profile : profileIds) 559 { 560 _removeProperty(aclNode, __PROPERTY_NAME_DENIED_ANONYMOUS_PROFILES, profile); 561 } 562 _save(node); 563 } 564 565 566 /* ------------------- */ 567 /* MANAGEMENT OF USERS */ 568 /* ------------------- */ 569 /** 570 * Helper for {@link ACLAmetysObject#getProfilesForUsers} 571 * @param node The JCR node for the Ametys object 572 * @param user The user to get profiles for. Can be null to get profiles for all users that have rights 573 * @return The map of allowed users with their assigned allowed/denied profiles 574 */ 575 public static Map<UserIdentity, Map<UserOrGroup, Set<String>>> getProfilesForUsers(Node node, UserIdentity user) 576 { 577 if (user == null) 578 { 579 try 580 { 581 Node usersNode = _getUsersNode(node); 582 if (usersNode == null) 583 { 584 return Map.of(); 585 } 586 587 Map<UserIdentity, Map<UserOrGroup, Set<String>>> result = new HashMap<>(); 588 589 NodeIterator populationsIterator = usersNode.getNodes(); 590 while (populationsIterator.hasNext()) 591 { 592 Node populationNode = populationsIterator.nextNode(); 593 NodeIterator usersIterator = populationNode.getNodes(); 594 while (usersIterator.hasNext()) 595 { 596 Node userNode = usersIterator.nextNode(); 597 Set<String> allowedProfiles = _getProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES); 598 Set<String> deniedProfiles = _getProperty(userNode, __PROPERTY_NAME_DENIED_PROFILES); 599 if (!allowedProfiles.isEmpty() || !deniedProfiles.isEmpty()) 600 { 601 result.put(new UserIdentity(userNode.getName(), populationNode.getName()), 602 Map.of(UserOrGroup.ALLOWED, allowedProfiles, 603 UserOrGroup.DENIED, deniedProfiles)); 604 } 605 } 606 } 607 608 return result; 609 } 610 catch (RepositoryException e) 611 { 612 throw new AmetysRepositoryException("Unable to get allowed/denied users", e); 613 } 614 } 615 else 616 { 617 Node userNode = _getUserNode(node, user); 618 if (userNode == null) 619 { 620 return Map.of(user, __USER_OR_GROUP_NORIGHT); 621 } 622 else 623 { 624 return Map.of(user, 625 Map.of(UserOrGroup.ALLOWED, _getProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES), 626 UserOrGroup.DENIED, _getProperty(userNode, __PROPERTY_NAME_DENIED_PROFILES))); 627 } 628 } 629 } 630 631 /** 632 * Helper for {@link ModifiableACLAmetysObject#addAllowedUsers(Set, String)} 633 * @param users The users to add 634 * @param node The JCR node for the Ametys object 635 * @param profileId The id of the profile 636 */ 637 public static void addAllowedUsers(Set<UserIdentity> users, Node node, String profileId) 638 { 639 for (UserIdentity userIdentity : users) 640 { 641 Node userNode = _getOrCreateUserNode(node, userIdentity); 642 _addProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES, profileId); 643 } 644 _save(node); 645 } 646 647 /** 648 * Helper for {@link ModifiableACLAmetysObject#removeAllowedUsers(Set, String)} 649 * @param users The users to remove 650 * @param node The JCR node for the Ametys object 651 * @param profileId The id of the profile 652 */ 653 public static void removeAllowedUsers(Set<UserIdentity> users, Node node, String profileId) 654 { 655 for (UserIdentity userIdentity : users) 656 { 657 Node userNode = _getOrCreateUserNode(node, userIdentity); 658 _removeProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES, profileId); 659 } 660 _save(node); 661 } 662 663 /** 664 * Helper for {@link ModifiableACLAmetysObject#removeAllowedGroups(Set)} 665 * @param users The users to remove 666 * @param node The JCR node for the Ametys object 667 */ 668 public static void removeAllowedUsers(Set<UserIdentity> users, Node node) 669 { 670 for (UserIdentity userIdentity : users) 671 { 672 Node userNode = _getOrCreateUserNode(node, userIdentity); 673 _setProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES, Collections.EMPTY_SET); 674 } 675 _save(node); 676 } 677 678 /** 679 * Helper for {@link ModifiableACLAmetysObject#addDeniedUsers(Set, String)} 680 * @param users The users to add 681 * @param node The JCR node for the Ametys object 682 * @param profileId The id of the profile 683 */ 684 public static void addDeniedUsers(Set<UserIdentity> users, Node node, String profileId) 685 { 686 for (UserIdentity userIdentity : users) 687 { 688 Node userNode = _getOrCreateUserNode(node, userIdentity); 689 _addProperty(userNode, __PROPERTY_NAME_DENIED_PROFILES, profileId); 690 } 691 _save(node); 692 } 693 694 /** 695 * Helper for {@link ModifiableACLAmetysObject#removeDeniedUsers(Set, String)} 696 * @param users The users to remove 697 * @param node The JCR node for the Ametys object 698 * @param profileId The id of the profile 699 */ 700 public static void removeDeniedUsers(Set<UserIdentity> users, Node node, String profileId) 701 { 702 for (UserIdentity userIdentity : users) 703 { 704 Node userNode = _getOrCreateUserNode(node, userIdentity); 705 _removeProperty(userNode, __PROPERTY_NAME_DENIED_PROFILES, profileId); 706 } 707 _save(node); 708 } 709 710 /** 711 * Helper for {@link ModifiableACLAmetysObject#removeDeniedUsers(Set)} 712 * @param users The users to remove 713 * @param node The JCR node for the Ametys object 714 */ 715 public static void removeDeniedUsers(Set<UserIdentity> users, Node node) 716 { 717 for (UserIdentity userIdentity : users) 718 { 719 Node userNode = _getOrCreateUserNode(node, userIdentity); 720 _setProperty(userNode, __PROPERTY_NAME_DENIED_PROFILES, Collections.EMPTY_SET); 721 } 722 _save(node); 723 } 724 725 /* -------------------- */ 726 /* MANAGEMENT OF GROUPS */ 727 /* -------------------- */ 728 729 /** 730 * Helper for {@link ACLAmetysObject#getProfilesForGroups} 731 * @param node The JCR node for the Ametys object 732 * @param groups The group to get profiles for. Can be null to get profiles for all groups that have rights 733 * @return The map of allowed/denied groups with their assigned profiles 734 */ 735 public static Map<GroupIdentity, Map<UserOrGroup, Set<String>>> getProfilesForGroups(Node node, Set<GroupIdentity> groups) 736 { 737 try 738 { 739 if (groups != null && groups.isEmpty()) 740 { 741 return Map.of(); 742 } 743 744 Node groupsNode = _getGroupsNode(node); 745 if (groupsNode == null) 746 { 747 return Map.of(); 748 } 749 750 Map<GroupIdentity, Map<UserOrGroup, Set<String>>> result = new HashMap<>(); 751 752 if (groups == null) 753 { 754 NodeIterator groupDirectoriesIterator = groupsNode.getNodes(); 755 while (groupDirectoriesIterator.hasNext()) 756 { 757 Node groupDirectoryNode = groupDirectoriesIterator.nextNode(); 758 NodeIterator groupsIterator = groupDirectoryNode.getNodes(); 759 while (groupsIterator.hasNext()) 760 { 761 Node groupNode = groupsIterator.nextNode(); 762 Set<String> allowedProfiles = _getProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES); 763 Set<String> deniedProfiles = _getProperty(groupNode, __PROPERTY_NAME_DENIED_PROFILES); 764 if (!allowedProfiles.isEmpty() || !deniedProfiles.isEmpty()) 765 { 766 result.put(new GroupIdentity(Text.unescapeIllegalJcrChars(groupNode.getName()), groupDirectoryNode.getName()), 767 Map.of(UserOrGroup.ALLOWED, allowedProfiles, 768 UserOrGroup.DENIED, deniedProfiles)); 769 } 770 } 771 } 772 } 773 else 774 { 775 Map<String, Node> groupsNodeByDirectoryIdCache = new HashMap<>(); 776 777 for (GroupIdentity group : groups) 778 { 779 Node directoryNode = groupsNodeByDirectoryIdCache.computeIfAbsent(group.getDirectoryId(), LambdaUtils.wrap(directoryId -> groupsNode.hasNode(directoryId) ? groupsNode.getNode(directoryId) : null)); 780 781 String groupNodeName = Text.escapeIllegalJcrChars(group.getId()); 782 if (directoryNode != null && directoryNode.hasNode(groupNodeName)) 783 { 784 Node groupNode = directoryNode.getNode(groupNodeName); 785 786 result.put(group, 787 Map.of(UserOrGroup.ALLOWED, _getProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES), 788 UserOrGroup.DENIED, _getProperty(groupNode, __PROPERTY_NAME_DENIED_PROFILES))); 789 } 790 } 791 } 792 793 return result; 794 } 795 catch (RepositoryException e) 796 { 797 throw new AmetysRepositoryException("Unable to get allowed/denied groups", e); 798 } 799 } 800 801 /** 802 * Helper for {@link ModifiableACLAmetysObject#addAllowedGroups(Set, String)} 803 * @param groups The groups to add 804 * @param node The JCR node for the Ametys object 805 * @param profileId The id of the profile 806 */ 807 public static void addAllowedGroups(Set<GroupIdentity> groups, Node node, String profileId) 808 { 809 for (GroupIdentity groupIdentity : groups) 810 { 811 Node groupNode = _getOrCreateGroupNode(node, groupIdentity); 812 _addProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES, profileId); 813 } 814 _save(node); 815 } 816 817 /** 818 * Helper for {@link ModifiableACLAmetysObject#removeAllowedGroups(Set, String)} 819 * @param groups The groups to remove 820 * @param node The JCR node for the Ametys object 821 * @param profileId The id of the profile 822 */ 823 public static void removeAllowedGroups(Set<GroupIdentity> groups, Node node, String profileId) 824 { 825 for (GroupIdentity groupIdentity : groups) 826 { 827 Node groupNode = _getOrCreateGroupNode(node, groupIdentity); 828 _removeProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES, profileId); 829 } 830 _save(node); 831 } 832 833 /** 834 * Helper for {@link ModifiableACLAmetysObject#removeAllowedGroups(Set)} 835 * @param groups The groups to remove 836 * @param node The JCR node for the Ametys object 837 */ 838 public static void removeAllowedGroups(Set<GroupIdentity> groups, Node node) 839 { 840 for (GroupIdentity groupIdentity : groups) 841 { 842 Node groupNode = _getOrCreateGroupNode(node, groupIdentity); 843 _setProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES, Collections.EMPTY_SET); 844 } 845 _save(node); 846 } 847 848 /** 849 * Helper for {@link ModifiableACLAmetysObject#addDeniedGroups(Set, String)} 850 * @param groups The groups to add 851 * @param node The JCR node for the Ametys object 852 * @param profileId The id of the profile 853 */ 854 public static void addDeniedGroups(Set<GroupIdentity> groups, Node node, String profileId) 855 { 856 for (GroupIdentity groupIdentity : groups) 857 { 858 Node groupNode = _getOrCreateGroupNode(node, groupIdentity); 859 _addProperty(groupNode, __PROPERTY_NAME_DENIED_PROFILES, profileId); 860 } 861 _save(node); 862 } 863 864 /** 865 * Helper for {@link ModifiableACLAmetysObject#removeDeniedGroups(Set, String)} 866 * @param groups The groups to remove 867 * @param node The JCR node for the Ametys object 868 * @param profileId The id of the profile 869 */ 870 public static void removeDeniedGroups(Set<GroupIdentity> groups, Node node, String profileId) 871 { 872 for (GroupIdentity groupIdentity : groups) 873 { 874 Node groupNode = _getOrCreateGroupNode(node, groupIdentity); 875 _removeProperty(groupNode, __PROPERTY_NAME_DENIED_PROFILES, profileId); 876 } 877 _save(node); 878 } 879 880 /** 881 * Helper for {@link ModifiableACLAmetysObject#removeDeniedGroups(Set)} 882 * @param groups The groups to remove 883 * @param node The JCR node for the Ametys object 884 */ 885 public static void removeDeniedGroups(Set<GroupIdentity> groups, Node node) 886 { 887 for (GroupIdentity groupIdentity : groups) 888 { 889 Node groupNode = _getOrCreateGroupNode(node, groupIdentity); 890 _setProperty(groupNode, __PROPERTY_NAME_DENIED_PROFILES, Collections.EMPTY_SET); 891 } 892 _save(node); 893 } 894 895 896 /* ------ */ 897 /* REMOVE */ 898 /* ------ */ 899 900 /** 901 * Helper for {@link ModifiableACLAmetysObjectProfileAssignmentStorage#removeProfile(String)} 902 * @param profileId The id of the profile 903 */ 904 public static void removeProfile(String profileId) 905 { 906 // Remove this profile set as allowed or denied in users 907 Expression expr = new OrExpression(new AllowedProfileExpression(profileId), new DeniedProfileExpression(profileId)); 908 NodeIterator users = getACLUsers(expr); 909 while (users.hasNext()) 910 { 911 Node userNode = (Node) users.next(); 912 _removeProperty(userNode, __PROPERTY_NAME_ALLOWED_PROFILES, profileId); 913 _removeProperty(userNode, __PROPERTY_NAME_DENIED_PROFILES, profileId); 914 _save(userNode); 915 } 916 917 // Remove this profile set as allowed or denied in groups 918 NodeIterator groups = getACLGroups(expr); 919 while (groups.hasNext()) 920 { 921 Node groupNode = (Node) groups.next(); 922 _removeProperty(groupNode, __PROPERTY_NAME_ALLOWED_PROFILES, profileId); 923 _removeProperty(groupNode, __PROPERTY_NAME_DENIED_PROFILES, profileId); 924 _save(groupNode); 925 } 926 927 // Remove this profile set as allowed or denied for anonymous and any connected 928 expr = new OrExpression(new AnonymousAllowedProfileExpression(profileId), new AnonymousDeniedProfileExpression(profileId), new AnyConnectedAllowedProfileExpression(profileId), new AnyConnectedDeniedProfileExpression(profileId)); 929 NodeIterator nodes = getACLRoots(null, expr); 930 while (nodes.hasNext()) 931 { 932 Node node = (Node) nodes.next(); 933 _removeProperty(node, __PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES, profileId); 934 _removeProperty(node, __PROPERTY_NAME_DENIED_ANY_CONNECTED_PROFILES, profileId); 935 _removeProperty(node, __PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES, profileId); 936 _removeProperty(node, __PROPERTY_NAME_DENIED_ANONYMOUS_PROFILES, profileId); 937 _save(node); 938 } 939 } 940 941 /** 942 * Helper for {@link ModifiableACLAmetysObjectProfileAssignmentStorage#removeUser(UserIdentity)} 943 * @param user The user 944 */ 945 public static void removeUser(UserIdentity user) 946 { 947 NodeIterator users = getACLUsers(user, null, null); 948 949 while (users.hasNext()) 950 { 951 Node userNode = (Node) users.next(); 952 try 953 { 954 userNode.remove(); 955 _save(userNode); 956 } 957 catch (RepositoryException e) 958 { 959 throw new AmetysRepositoryException(e); 960 } 961 } 962 } 963 964 /** 965 * Helper for {@link ModifiableACLAmetysObjectProfileAssignmentStorage#removeGroup(GroupIdentity)} 966 * @param group The group 967 */ 968 public static void removeGroup(GroupIdentity group) 969 { 970 NodeIterator groups = getACLGroups(group, null, null); 971 while (groups.hasNext()) 972 { 973 Node gpNode = (Node) groups.next(); 974 try 975 { 976 gpNode.remove(); 977 _save(gpNode); 978 } 979 catch (RepositoryException e) 980 { 981 throw new AmetysRepositoryException(e); 982 } 983 } 984 } 985 986 /* --------------- */ 987 /* INHERITANCE */ 988 /* --------------- */ 989 /** 990 * Helper for {@link ACLAmetysObject#isInheritanceDisallowed()} 991 * @param node The JCR node for the Ametys object 992 * @return true if the inheritance is disallow of the given node 993 */ 994 public static boolean isInheritanceDisallowed(Node node) 995 { 996 try 997 { 998 Node aclNode = _getACLNode(node); 999 if (aclNode != null && aclNode.hasProperty(__PROPERTY_NAME_DISALLOW_INHERITANCE)) 1000 { 1001 return aclNode.getProperty(__PROPERTY_NAME_DISALLOW_INHERITANCE).getBoolean(); 1002 } 1003 return false; 1004 } 1005 catch (RepositoryException e) 1006 { 1007 throw new AmetysRepositoryException("Unable to get " + __PROPERTY_NAME_DISALLOW_INHERITANCE + " property", e); 1008 } 1009 } 1010 1011 /** 1012 * Helper for {@link ModifiableACLAmetysObject#disallowInheritance(boolean)} 1013 * @param node The JCR node for the Ametys object 1014 * @param disallow true to disallow the inheritance, false otherwise 1015 */ 1016 public static void disallowInheritance(Node node, boolean disallow) 1017 { 1018 Node aclNode = _getOrCreateACLNode(node); 1019 try 1020 { 1021 aclNode.setProperty(__PROPERTY_NAME_DISALLOW_INHERITANCE, disallow); 1022 } 1023 catch (RepositoryException e) 1024 { 1025 throw new AmetysRepositoryException("Unable to set " + __PROPERTY_NAME_DISALLOW_INHERITANCE + " property", e); 1026 } 1027 _save(node); 1028 } 1029 1030 1031 /* --------------- */ 1032 /* PRIVATE METHODS */ 1033 /* --------------- */ 1034 1035 private static void _checkLock(Node node) throws AmetysRepositoryException 1036 { 1037 try 1038 { 1039 if (node.isLocked()) 1040 { 1041 LockManager lockManager = node.getSession().getWorkspace().getLockManager(); 1042 1043 Lock lock = lockManager.getLock(node.getPath()); 1044 Node lockHolder = lock.getNode(); 1045 1046 lockManager.addLockToken(lockHolder.getProperty(RepositoryConstants.METADATA_LOCKTOKEN).getString()); 1047 } 1048 } 1049 catch (RepositoryException e) 1050 { 1051 throw new AmetysRepositoryException("Unable to add lock token on ACL node", e); 1052 } 1053 } 1054 1055 private static Node _getOrCreateACLNode(Node node) 1056 { 1057 try 1058 { 1059 if (node.hasNode(__NODE_NAME_ROOT_ACL)) 1060 { 1061 return node.getNode(__NODE_NAME_ROOT_ACL); 1062 } 1063 else 1064 { 1065 _checkLock(node); 1066 return node.addNode(__NODE_NAME_ROOT_ACL, __NODETYPE_ROOT_ACL); 1067 } 1068 } 1069 catch (RepositoryException e) 1070 { 1071 throw new AmetysRepositoryException("Error while getting root ACL node.", e); 1072 } 1073 } 1074 1075 private static Node _getACLNode(Node node) 1076 { 1077 try 1078 { 1079 if (node.hasNode(__NODE_NAME_ROOT_ACL)) 1080 { 1081 return node.getNode(__NODE_NAME_ROOT_ACL); 1082 } 1083 else 1084 { 1085 return null; 1086 } 1087 } 1088 catch (RepositoryException e) 1089 { 1090 throw new AmetysRepositoryException("Error while getting root ACL node.", e); 1091 } 1092 } 1093 1094 private static Node _getOrCreateUsersNode(Node node) 1095 { 1096 try 1097 { 1098 Node aclNode = _getOrCreateACLNode(node); 1099 if (aclNode.hasNode(__NODE_NAME_ACL_USERS)) 1100 { 1101 return aclNode.getNode(__NODE_NAME_ACL_USERS); 1102 } 1103 else 1104 { 1105 return aclNode.addNode(__NODE_NAME_ACL_USERS, __NODETYPE_UNSTRUCTURED); 1106 } 1107 } 1108 catch (RepositoryException e) 1109 { 1110 throw new AmetysRepositoryException("Error while getting 'users' ACL node.", e); 1111 } 1112 } 1113 1114 private static Node _getUserNode(Node node, UserIdentity user) 1115 { 1116 try 1117 { 1118 Node aclNode = _getACLNode(node); 1119 if (aclNode != null && aclNode.hasNode(__NODE_NAME_ACL_USERS)) 1120 { 1121 Node aclUsersNode = aclNode.getNode(__NODE_NAME_ACL_USERS); 1122 if (aclUsersNode.hasNode(user.getPopulationId())) 1123 { 1124 Node popNode = aclUsersNode.getNode(user.getPopulationId()); 1125 if (popNode.hasNode(user.getLogin())) 1126 { 1127 return popNode.getNode(user.getLogin()); 1128 } 1129 } 1130 } 1131 1132 return null; 1133 } 1134 catch (RepositoryException e) 1135 { 1136 throw new AmetysRepositoryException("Error while getting 'users' ACL node.", e); 1137 } 1138 } 1139 1140 private static Node _getUsersNode(Node node) 1141 { 1142 try 1143 { 1144 Node aclNode = _getACLNode(node); 1145 if (aclNode != null && aclNode.hasNode(__NODE_NAME_ACL_USERS)) 1146 { 1147 return aclNode.getNode(__NODE_NAME_ACL_USERS); 1148 } 1149 else 1150 { 1151 return null; 1152 } 1153 } 1154 catch (RepositoryException e) 1155 { 1156 throw new AmetysRepositoryException("Error while getting 'users' ACL node.", e); 1157 } 1158 } 1159 1160 private static Node _getOrCreateGroupsNode(Node node) 1161 { 1162 try 1163 { 1164 Node aclNode = _getOrCreateACLNode(node); 1165 if (aclNode.hasNode(__NODE_NAME_ACL_GROUPS)) 1166 { 1167 return aclNode.getNode(__NODE_NAME_ACL_GROUPS); 1168 } 1169 else 1170 { 1171 return aclNode.addNode(__NODE_NAME_ACL_GROUPS, __NODETYPE_UNSTRUCTURED); 1172 } 1173 } 1174 catch (RepositoryException e) 1175 { 1176 throw new AmetysRepositoryException("Error while getting 'groups' ACL node.", e); 1177 } 1178 } 1179 1180 private static Node _getGroupsNode(Node node) 1181 { 1182 try 1183 { 1184 Node aclNode = _getACLNode(node); 1185 if (aclNode != null && aclNode.hasNode(__NODE_NAME_ACL_GROUPS)) 1186 { 1187 return aclNode.getNode(__NODE_NAME_ACL_GROUPS); 1188 } 1189 else 1190 { 1191 return null; 1192 } 1193 } 1194 catch (RepositoryException e) 1195 { 1196 throw new AmetysRepositoryException("Error while getting 'groups' ACL node.", e); 1197 } 1198 } 1199 1200 private static Node _getOrCreateUserNode(Node node, UserIdentity userIdentity) 1201 { 1202 try 1203 { 1204 Node usersNode = _getOrCreateUsersNode(node); 1205 String population = userIdentity.getPopulationId(); 1206 String login = userIdentity.getLogin(); 1207 1208 if (usersNode.hasNode(population)) 1209 { 1210 Node populationNode = usersNode.getNode(population); 1211 if (populationNode.hasNode(login)) 1212 { 1213 return populationNode.getNode(login); 1214 } 1215 else 1216 { 1217 return populationNode.addNode(login, __NODETYPE_ACL_USER); 1218 } 1219 } 1220 else 1221 { 1222 return usersNode.addNode(population, __NODETYPE_UNSTRUCTURED).addNode(login, __NODETYPE_ACL_USER); 1223 } 1224 } 1225 catch (RepositoryException e) 1226 { 1227 throw new AmetysRepositoryException(String.format("Error while getting 'user' ACL node for %s.", userIdentity.toString()), e); 1228 } 1229 } 1230 1231 private static Node _getOrCreateGroupNode(Node node, GroupIdentity groupIdentity) 1232 { 1233 try 1234 { 1235 Node groupsNode = _getOrCreateGroupsNode(node); 1236 String directoryId = groupIdentity.getDirectoryId(); 1237 String id = Text.escapeIllegalJcrChars(groupIdentity.getId()); 1238 1239 if (groupsNode.hasNode(directoryId)) 1240 { 1241 Node populationNode = groupsNode.getNode(directoryId); 1242 if (populationNode.hasNode(id)) 1243 { 1244 return populationNode.getNode(id); 1245 } 1246 else 1247 { 1248 return populationNode.addNode(id, __NODETYPE_ACL_GROUP); 1249 } 1250 } 1251 else 1252 { 1253 return groupsNode.addNode(directoryId, __NODETYPE_UNSTRUCTURED).addNode(id, __NODETYPE_ACL_GROUP); 1254 } 1255 } 1256 catch (RepositoryException e) 1257 { 1258 throw new AmetysRepositoryException(String.format("Error while getting 'group' ACL node for %s.", groupIdentity.toString()), e); 1259 } 1260 } 1261 1262 private static Set<String> _getProperty(Node node, String propertyName) 1263 { 1264 try 1265 { 1266 Value[] values = node.getProperty(propertyName).getValues(); 1267 Set<String> result = new HashSet<>(); 1268 for (Value value : values) 1269 { 1270 result.add(value.getString()); 1271 } 1272 return result; 1273 } 1274 catch (PathNotFoundException e) 1275 { 1276 return new HashSet<>(); 1277 } 1278 catch (RepositoryException e) 1279 { 1280 throw new AmetysRepositoryException("Unable to get " + propertyName + " property", e); 1281 } 1282 } 1283 1284 private static void _setProperty(Node node, String propertyName, Set<String> profiles) 1285 { 1286 try 1287 { 1288 node.setProperty(propertyName, profiles.toArray(new String[profiles.size()])); 1289 } 1290 catch (RepositoryException e) 1291 { 1292 throw new AmetysRepositoryException("Unable to set " + propertyName + " property", e); 1293 } 1294 } 1295 1296 private static void _addProperty(Node node, String propertyName, String profileToAdd) 1297 { 1298 Set<String> profiles = _getProperty(node, propertyName); 1299 if (!profiles.contains(profileToAdd)) 1300 { 1301 profiles.add(profileToAdd); 1302 _setProperty(node, propertyName, profiles); 1303 } 1304 } 1305 1306 private static void _removeProperty(Node node, String propertyName, String profileToRemove) 1307 { 1308 Set<String> profiles = _getProperty(node, propertyName); 1309 if (profiles.contains(profileToRemove)) 1310 { 1311 profiles.remove(profileToRemove); 1312 _setProperty(node, propertyName, profiles); 1313 } 1314 } 1315 1316 private static void _save(Node node) 1317 { 1318 try 1319 { 1320 node.getSession().save(); 1321 } 1322 catch (RepositoryException e) 1323 { 1324 throw new AmetysRepositoryException("Unable to save changes", e); 1325 } 1326 } 1327 1328 /* ---------------------------------------*/ 1329 /* JCR EXPRESSIONS FOR PROFILES */ 1330 /* ---------------------------------------*/ 1331 1332 static class AllowedProfileExpression extends ACLProfileExpression 1333 { 1334 public AllowedProfileExpression (String ... profileIds) 1335 { 1336 super(__PROPERTY_NAME_ALLOWED_PROFILES, profileIds); 1337 } 1338 } 1339 1340 static class DeniedProfileExpression extends ACLProfileExpression 1341 { 1342 public DeniedProfileExpression (String ... profileIds) 1343 { 1344 super(__PROPERTY_NAME_DENIED_PROFILES, profileIds); 1345 } 1346 } 1347 1348 static class AnyConnectedDeniedProfileExpression extends ACLProfileExpression 1349 { 1350 public AnyConnectedDeniedProfileExpression (String ... profileIds) 1351 { 1352 super(__PROPERTY_NAME_DENIED_ANY_CONNECTED_PROFILES, profileIds); 1353 } 1354 } 1355 1356 static class AnyConnectedAllowedProfileExpression extends ACLProfileExpression 1357 { 1358 public AnyConnectedAllowedProfileExpression (String ... profileIds) 1359 { 1360 super(__PROPERTY_NAME_ALLOWED_ANY_CONNECTED_PROFILES, profileIds); 1361 } 1362 } 1363 1364 static class AnonymousDeniedProfileExpression extends ACLProfileExpression 1365 { 1366 public AnonymousDeniedProfileExpression (String ... profileIds) 1367 { 1368 super(__PROPERTY_NAME_DENIED_ANONYMOUS_PROFILES, profileIds); 1369 } 1370 } 1371 1372 static class AnonymousAllowedProfileExpression extends ACLProfileExpression 1373 { 1374 public AnonymousAllowedProfileExpression (String ... profileIds) 1375 { 1376 super(__PROPERTY_NAME_ALLOWED_ANONYMOUS_PROFILES, profileIds); 1377 } 1378 } 1379 1380 static class ACLProfileExpression implements Expression 1381 { 1382 private String[] _profileIds; 1383 private String _propertyName; 1384 1385 public ACLProfileExpression (String propertyName, String ... profileIds) 1386 { 1387 _propertyName = propertyName; 1388 _profileIds = profileIds; 1389 } 1390 1391 @Override 1392 public String build() 1393 { 1394 boolean isFirst = true; 1395 StringBuilder sb = new StringBuilder("("); 1396 1397 for (String profileId : _profileIds) 1398 { 1399 if (isFirst) 1400 { 1401 isFirst = false; 1402 } 1403 else 1404 { 1405 sb.append(" or "); 1406 } 1407 1408 sb.append("@") 1409 .append(_propertyName) 1410 .append(Operator.EQ) 1411 .append("'").append(profileId).append("'"); 1412 } 1413 1414 if (isFirst) 1415 { 1416 return ""; 1417 } 1418 else 1419 { 1420 return sb.append(")").toString(); 1421 } 1422 } 1423 } 1424}