001/* 002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * George Lefter 016 * Florent Guillaume 017 * Anahide Tchertchian 018 * Gagnavarslan ehf 019 */ 020package org.nuxeo.ecm.platform.usermanager; 021 022import java.io.IOException; 023import java.io.Serializable; 024import java.security.Principal; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.LinkedList; 031import java.util.List; 032import java.util.Map; 033import java.util.Map.Entry; 034import java.util.Set; 035import java.util.regex.Matcher; 036import java.util.regex.Pattern; 037 038import org.apache.commons.codec.digest.DigestUtils; 039import org.apache.commons.lang.StringUtils; 040import org.apache.commons.logging.Log; 041import org.apache.commons.logging.LogFactory; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentModelComparator; 044import org.nuxeo.ecm.core.api.DocumentModelList; 045import org.nuxeo.ecm.core.api.NuxeoException; 046import org.nuxeo.ecm.core.api.NuxeoGroup; 047import org.nuxeo.ecm.core.api.NuxeoPrincipal; 048import org.nuxeo.ecm.core.api.PropertyException; 049import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 050import org.nuxeo.ecm.core.api.impl.NuxeoGroupImpl; 051import org.nuxeo.ecm.core.api.model.PropertyNotFoundException; 052import org.nuxeo.ecm.core.api.security.ACE; 053import org.nuxeo.ecm.core.api.security.ACL; 054import org.nuxeo.ecm.core.api.security.ACP; 055import org.nuxeo.ecm.core.api.security.AdministratorGroupsProvider; 056import org.nuxeo.ecm.core.api.security.PermissionProvider; 057import org.nuxeo.ecm.core.api.security.SecurityConstants; 058import org.nuxeo.ecm.core.cache.Cache; 059import org.nuxeo.ecm.core.cache.CacheService; 060import org.nuxeo.ecm.directory.BaseSession; 061import org.nuxeo.ecm.directory.DirectoryException; 062import org.nuxeo.ecm.directory.Session; 063import org.nuxeo.ecm.directory.api.DirectoryService; 064import org.nuxeo.ecm.platform.usermanager.exceptions.GroupAlreadyExistsException; 065import org.nuxeo.ecm.platform.usermanager.exceptions.UserAlreadyExistsException; 066import org.nuxeo.runtime.api.Framework; 067import org.nuxeo.runtime.services.event.Event; 068import org.nuxeo.runtime.services.event.EventService; 069 070/** 071 * Standard implementation of the Nuxeo UserManager. 072 */ 073public class UserManagerImpl implements UserManager, MultiTenantUserManager, AdministratorGroupsProvider { 074 075 private static final long serialVersionUID = 1L; 076 077 private static final Log log = LogFactory.getLog(UserManagerImpl.class); 078 079 public static final String USERMANAGER_TOPIC = "usermanager"; 080 081 /** Used by JaasCacheFlusher. */ 082 public static final String USERCHANGED_EVENT_ID = "user_changed"; 083 084 public static final String USERCREATED_EVENT_ID = "user_created"; 085 086 public static final String USERDELETED_EVENT_ID = "user_deleted"; 087 088 public static final String USERMODIFIED_EVENT_ID = "user_modified"; 089 090 /** Used by JaasCacheFlusher. */ 091 public static final String GROUPCHANGED_EVENT_ID = "group_changed"; 092 093 public static final String GROUPCREATED_EVENT_ID = "group_created"; 094 095 public static final String GROUPDELETED_EVENT_ID = "group_deleted"; 096 097 public static final String GROUPMODIFIED_EVENT_ID = "group_modified"; 098 099 public static final String DEFAULT_ANONYMOUS_USER_ID = "Anonymous"; 100 101 public static final String VIRTUAL_FIELD_FILTER_PREFIX = "__"; 102 103 public static final String INVALIDATE_PRINCIPAL_EVENT_ID = "invalidatePrincipal"; 104 105 public static final String INVALIDATE_ALL_PRINCIPALS_EVENT_ID = "invalidateAllPrincipals"; 106 107 protected final DirectoryService dirService; 108 109 protected final CacheService cacheService; 110 111 protected Cache principalCache = null; 112 113 public UserMultiTenantManagement multiTenantManagement = new DefaultUserMultiTenantManagement(); 114 115 /** 116 * A structure used to inject field name configuration of users schema into a NuxeoPrincipalImpl instance. TODO not 117 * all fields inside are configurable for now - they will use default values 118 */ 119 protected UserConfig userConfig; 120 121 protected String userDirectoryName; 122 123 protected String userSchemaName; 124 125 protected String userIdField; 126 127 protected String userEmailField; 128 129 protected Map<String, MatchType> userSearchFields; 130 131 protected String groupDirectoryName; 132 133 protected String groupSchemaName; 134 135 protected String groupIdField; 136 137 protected String groupLabelField; 138 139 protected String groupMembersField; 140 141 protected String groupSubGroupsField; 142 143 protected String groupParentGroupsField; 144 145 protected String groupSortField; 146 147 protected Map<String, MatchType> groupSearchFields; 148 149 protected String defaultGroup; 150 151 protected List<String> administratorIds; 152 153 protected List<String> administratorGroups; 154 155 protected Boolean disableDefaultAdministratorsGroup; 156 157 protected String userSortField; 158 159 protected String userListingMode; 160 161 protected String groupListingMode; 162 163 protected Pattern userPasswordPattern; 164 165 protected VirtualUser anonymousUser; 166 167 protected String digestAuthDirectory; 168 169 protected String digestAuthRealm; 170 171 protected final Map<String, VirtualUserDescriptor> virtualUsers; 172 173 public UserManagerImpl() { 174 dirService = Framework.getLocalService(DirectoryService.class); 175 cacheService = Framework.getLocalService(CacheService.class); 176 virtualUsers = new HashMap<String, VirtualUserDescriptor>(); 177 userConfig = new UserConfig(); 178 } 179 180 @Override 181 public void setConfiguration(UserManagerDescriptor descriptor) { 182 defaultGroup = descriptor.defaultGroup; 183 administratorIds = descriptor.defaultAdministratorIds; 184 disableDefaultAdministratorsGroup = false; 185 if (descriptor.disableDefaultAdministratorsGroup != null) { 186 disableDefaultAdministratorsGroup = descriptor.disableDefaultAdministratorsGroup; 187 } 188 administratorGroups = new ArrayList<String>(); 189 if (!disableDefaultAdministratorsGroup) { 190 administratorGroups.add(SecurityConstants.ADMINISTRATORS); 191 } 192 if (descriptor.administratorsGroups != null) { 193 administratorGroups.addAll(descriptor.administratorsGroups); 194 } 195 if (administratorGroups.isEmpty()) { 196 log.warn("No administrators group has been defined: at least one should be set" 197 + " to avoid lockups when blocking rights for instance"); 198 } 199 userSortField = descriptor.userSortField; 200 groupSortField = descriptor.groupSortField; 201 userListingMode = descriptor.userListingMode; 202 groupListingMode = descriptor.groupListingMode; 203 userEmailField = descriptor.userEmailField; 204 userSearchFields = descriptor.userSearchFields; 205 userPasswordPattern = descriptor.userPasswordPattern; 206 groupLabelField = descriptor.groupLabelField; 207 groupMembersField = descriptor.groupMembersField; 208 groupSubGroupsField = descriptor.groupSubGroupsField; 209 groupParentGroupsField = descriptor.groupParentGroupsField; 210 groupSearchFields = descriptor.groupSearchFields; 211 anonymousUser = descriptor.anonymousUser; 212 213 setUserDirectoryName(descriptor.userDirectoryName); 214 setGroupDirectoryName(descriptor.groupDirectoryName); 215 setVirtualUsers(descriptor.virtualUsers); 216 217 digestAuthDirectory = descriptor.digestAuthDirectory; 218 digestAuthRealm = descriptor.digestAuthRealm; 219 220 userConfig = new UserConfig(); 221 userConfig.emailKey = userEmailField; 222 userConfig.schemaName = userSchemaName; 223 userConfig.nameKey = userIdField; 224 225 if (cacheService != null && descriptor.userCacheName != null) { 226 principalCache = cacheService.getCache(descriptor.userCacheName); 227 } 228 229 } 230 231 protected void setUserDirectoryName(String userDirectoryName) { 232 this.userDirectoryName = userDirectoryName; 233 userSchemaName = dirService.getDirectorySchema(userDirectoryName); 234 userIdField = dirService.getDirectoryIdField(userDirectoryName); 235 } 236 237 @Override 238 public String getUserDirectoryName() { 239 return userDirectoryName; 240 } 241 242 @Override 243 public String getUserIdField() { 244 return userIdField; 245 } 246 247 @Override 248 public String getUserSchemaName() { 249 return userSchemaName; 250 } 251 252 @Override 253 public String getUserEmailField() { 254 return userEmailField; 255 } 256 257 @Override 258 public Set<String> getUserSearchFields() { 259 return Collections.unmodifiableSet(userSearchFields.keySet()); 260 } 261 262 @Override 263 public Set<String> getGroupSearchFields() { 264 return Collections.unmodifiableSet(groupSearchFields.keySet()); 265 } 266 267 protected void setGroupDirectoryName(String groupDirectoryName) { 268 this.groupDirectoryName = groupDirectoryName; 269 groupSchemaName = dirService.getDirectorySchema(groupDirectoryName); 270 groupIdField = dirService.getDirectoryIdField(groupDirectoryName); 271 } 272 273 @Override 274 public String getGroupDirectoryName() { 275 return groupDirectoryName; 276 } 277 278 @Override 279 public String getGroupIdField() { 280 return groupIdField; 281 } 282 283 @Override 284 public String getGroupLabelField() { 285 return groupLabelField; 286 } 287 288 @Override 289 public String getGroupSchemaName() { 290 return groupSchemaName; 291 } 292 293 @Override 294 public String getGroupMembersField() { 295 return groupMembersField; 296 } 297 298 @Override 299 public String getGroupSubGroupsField() { 300 return groupSubGroupsField; 301 } 302 303 @Override 304 public String getGroupParentGroupsField() { 305 return groupParentGroupsField; 306 } 307 308 @Override 309 public String getUserListingMode() { 310 return userListingMode; 311 } 312 313 @Override 314 public String getGroupListingMode() { 315 return groupListingMode; 316 } 317 318 @Override 319 public String getDefaultGroup() { 320 return defaultGroup; 321 } 322 323 @Override 324 public Pattern getUserPasswordPattern() { 325 return userPasswordPattern; 326 } 327 328 @Override 329 public String getAnonymousUserId() { 330 if (anonymousUser == null) { 331 return null; 332 } 333 String anonymousUserId = anonymousUser.getId(); 334 if (anonymousUserId == null) { 335 return DEFAULT_ANONYMOUS_USER_ID; 336 } 337 return anonymousUserId; 338 } 339 340 protected void setVirtualUsers(Map<String, VirtualUserDescriptor> virtualUsers) { 341 this.virtualUsers.clear(); 342 if (virtualUsers != null) { 343 this.virtualUsers.putAll(virtualUsers); 344 } 345 } 346 347 @Override 348 public boolean checkUsernamePassword(String username, String password) { 349 350 if (username == null || password == null) { 351 log.warn("Trying to authenticate against null username or password"); 352 return false; 353 } 354 355 // deal with anonymous user 356 String anonymousUserId = getAnonymousUserId(); 357 if (username.equals(anonymousUserId)) { 358 log.warn(String.format("Trying to authenticate anonymous user (%s)", anonymousUserId)); 359 return false; 360 } 361 362 // deal with virtual users 363 if (virtualUsers.containsKey(username)) { 364 VirtualUser user = virtualUsers.get(username); 365 String expected = user.getPassword(); 366 if (expected == null) { 367 return false; 368 } 369 return expected.equals(password); 370 } 371 372 String userDirName; 373 // BBB backward compat for userDirectory + userAuthentication 374 if ("userDirectory".equals(userDirectoryName) && dirService.getDirectory("userAuthentication") != null) { 375 userDirName = "userAuthentication"; 376 } else { 377 userDirName = userDirectoryName; 378 } 379 try (Session userDir = dirService.open(userDirName)) { 380 if (!userDir.isAuthenticating()) { 381 log.error("Trying to authenticate against a non authenticating " + "directory: " + userDirName); 382 return false; 383 } 384 385 boolean authenticated = userDir.authenticate(username, password); 386 if (authenticated) { 387 syncDigestAuthPassword(username, password); 388 } 389 return authenticated; 390 } 391 } 392 393 protected void syncDigestAuthPassword(String username, String password) { 394 if (StringUtils.isEmpty(digestAuthDirectory) || StringUtils.isEmpty(digestAuthRealm) || username == null 395 || password == null) { 396 return; 397 } 398 399 String ha1 = encodeDigestAuthPassword(username, digestAuthRealm, password); 400 try (Session dir = dirService.open(digestAuthDirectory)) { 401 String schema = dirService.getDirectorySchema(digestAuthDirectory); 402 DocumentModel entry = dir.getEntry(username, true); 403 if (entry == null) { 404 entry = getDigestAuthModel(); 405 entry.setProperty(schema, dir.getIdField(), username); 406 entry.setProperty(schema, dir.getPasswordField(), ha1); 407 dir.createEntry(entry); 408 log.debug("Created digest auth password for user:" + username); 409 } else { 410 String storedHa1 = (String) entry.getProperty(schema, dir.getPasswordField()); 411 if (!ha1.equals(storedHa1)) { 412 entry.setProperty(schema, dir.getPasswordField(), ha1); 413 dir.updateEntry(entry); 414 log.debug("Updated digest auth password for user:" + username); 415 } 416 } 417 } catch (DirectoryException e) { 418 log.warn("Digest auth password not synchronized, check your configuration", e); 419 } 420 } 421 422 protected DocumentModel getDigestAuthModel() { 423 String schema = dirService.getDirectorySchema(digestAuthDirectory); 424 return BaseSession.createEntryModel(null, schema, null, null); 425 } 426 427 public static String encodeDigestAuthPassword(String username, String realm, String password) { 428 String a1 = username + ":" + realm + ":" + password; 429 return DigestUtils.md5Hex(a1); 430 } 431 432 @Override 433 public String getDigestAuthDirectory() { 434 return digestAuthDirectory; 435 } 436 437 @Override 438 public String getDigestAuthRealm() { 439 return digestAuthRealm; 440 } 441 442 @Override 443 public boolean validatePassword(String password) { 444 if (userPasswordPattern == null) { 445 return true; 446 } else { 447 Matcher userPasswordMatcher = userPasswordPattern.matcher(password); 448 return userPasswordMatcher.find(); 449 } 450 } 451 452 protected NuxeoPrincipal makeAnonymousPrincipal() { 453 DocumentModel userEntry = makeVirtualUserEntry(getAnonymousUserId(), anonymousUser); 454 // XXX: pass anonymous user groups, but they will be ignored 455 return makePrincipal(userEntry, true, anonymousUser.getGroups()); 456 } 457 458 protected NuxeoPrincipal makeVirtualPrincipal(VirtualUser user) { 459 DocumentModel userEntry = makeVirtualUserEntry(user.getId(), user); 460 return makePrincipal(userEntry, false, user.getGroups()); 461 } 462 463 protected DocumentModel makeVirtualUserEntry(String id, VirtualUser user) { 464 final DocumentModel userEntry = BaseSession.createEntryModel(null, userSchemaName, id, null); 465 // at least fill id field 466 userEntry.setProperty(userSchemaName, userIdField, id); 467 for (Entry<String, Serializable> prop : user.getProperties().entrySet()) { 468 try { 469 userEntry.setProperty(userSchemaName, prop.getKey(), prop.getValue()); 470 } catch (PropertyNotFoundException ce) { 471 log.error( 472 "Property: " + prop.getKey() + " does not exists. Check your " + "UserService configuration.", 473 ce); 474 } 475 } 476 return userEntry; 477 } 478 479 protected NuxeoPrincipal makePrincipal(DocumentModel userEntry) { 480 return makePrincipal(userEntry, false, null); 481 } 482 483 protected NuxeoPrincipal makePrincipal(DocumentModel userEntry, boolean anonymous, List<String> groups) { 484 boolean admin = false; 485 String username = userEntry.getId(); 486 487 List<String> virtualGroups = new LinkedList<String>(); 488 // Add preconfigured groups: useful for LDAP, not for anonymous users 489 if (defaultGroup != null && !anonymous) { 490 virtualGroups.add(defaultGroup); 491 } 492 // Add additional groups: useful for virtual users 493 if (groups != null) { 494 virtualGroups.addAll(groups); 495 } 496 // Create a default admin if needed 497 if (administratorIds != null && administratorIds.contains(username)) { 498 admin = true; 499 if (administratorGroups != null) { 500 virtualGroups.addAll(administratorGroups); 501 } 502 } 503 504 NuxeoPrincipalImpl principal = new NuxeoPrincipalImpl(username, anonymous, admin, false); 505 principal.setConfig(userConfig); 506 507 principal.setModel(userEntry, false); 508 principal.setVirtualGroups(virtualGroups, true); 509 510 // TODO: reenable roles initialization once we have a use case for 511 // a role directory. In the mean time we only set the JBOSS role 512 // that is required to login 513 List<String> roles = Arrays.asList("regular"); 514 principal.setRoles(roles); 515 516 return principal; 517 } 518 519 protected boolean useCache() { 520 return principalCache != null; 521 } 522 523 @Override 524 public NuxeoPrincipal getPrincipal(String username) { 525 NuxeoPrincipal principal = null; 526 if (useCache()) { 527 principal = (NuxeoPrincipal) principalCache.get(username); 528 } 529 if (principal == null) { 530 principal = getPrincipal(username, null); 531 if (useCache() && principal != null) { 532 principalCache.put(username, principal); 533 } 534 } 535 return principal; 536 } 537 538 @Override 539 public DocumentModel getUserModel(String userName) { 540 return getUserModel(userName, null); 541 } 542 543 @Override 544 public DocumentModel getBareUserModel() { 545 String schema = dirService.getDirectorySchema(userDirectoryName); 546 return BaseSession.createEntryModel(null, schema, null, null); 547 } 548 549 @Override 550 public NuxeoGroup getGroup(String groupName) { 551 return getGroup(groupName, null); 552 } 553 554 protected NuxeoGroup getGroup(String groupName, DocumentModel context) { 555 DocumentModel groupEntry = getGroupModel(groupName, context); 556 if (groupEntry != null) { 557 return makeGroup(groupEntry); 558 } 559 return null; 560 561 } 562 563 @Override 564 public DocumentModel getGroupModel(String groupName) { 565 return getGroupModel(groupName, null); 566 } 567 568 @SuppressWarnings("unchecked") 569 protected NuxeoGroup makeGroup(DocumentModel groupEntry) { 570 NuxeoGroup group = new NuxeoGroupImpl(groupEntry.getId()); 571 List<String> list; 572 try { 573 list = (List<String>) groupEntry.getProperty(groupSchemaName, groupMembersField); 574 } catch (PropertyException e) { 575 list = null; 576 } 577 if (list != null) { 578 group.setMemberUsers(list); 579 } 580 try { 581 list = (List<String>) groupEntry.getProperty(groupSchemaName, groupSubGroupsField); 582 } catch (PropertyException e) { 583 list = null; 584 } 585 if (list != null) { 586 group.setMemberGroups(list); 587 } 588 try { 589 list = (List<String>) groupEntry.getProperty(groupSchemaName, groupParentGroupsField); 590 } catch (PropertyException e) { 591 list = null; 592 } 593 if (list != null) { 594 group.setParentGroups(list); 595 } 596 try { 597 String label = (String) groupEntry.getProperty(groupSchemaName, groupLabelField); 598 if (label != null) { 599 group.setLabel(label); 600 } 601 } catch (PropertyException e) { 602 // Nothing to do. 603 } 604 return group; 605 } 606 607 @Override 608 public List<String> getTopLevelGroups() { 609 return getTopLevelGroups(null); 610 } 611 612 @Override 613 public List<String> getGroupsInGroup(String parentId) { 614 NuxeoGroup group = getGroup(parentId, null); 615 if (group != null) { 616 return group.getMemberGroups(); 617 } else { 618 return Collections.emptyList(); 619 } 620 } 621 622 @Override 623 public List<String> getUsersInGroup(String groupId) { 624 return getGroup(groupId).getMemberUsers(); 625 } 626 627 @Override 628 public List<String> getUsersInGroupAndSubGroups(String groupId) { 629 return getUsersInGroupAndSubGroups(groupId, null); 630 } 631 632 protected void appendSubgroups(String groupId, Set<String> groups, DocumentModel context) { 633 List<String> groupsToAppend = getGroupsInGroup(groupId, context); 634 groups.addAll(groupsToAppend); 635 for (String subgroupId : groupsToAppend) { 636 groups.add(subgroupId); 637 // avoiding infinite loop 638 if (!groups.contains(subgroupId)) { 639 appendSubgroups(subgroupId, groups, context); 640 } 641 } 642 643 } 644 645 protected boolean isAnonymousMatching(Map<String, Serializable> filter, Set<String> fulltext) { 646 String anonymousUserId = getAnonymousUserId(); 647 if (anonymousUserId == null) { 648 return false; 649 } 650 if (filter == null || filter.isEmpty()) { 651 return true; 652 } 653 Map<String, Serializable> anonymousUserMap = anonymousUser.getProperties(); 654 anonymousUserMap.put(userIdField, anonymousUserId); 655 for (Entry<String, Serializable> e : filter.entrySet()) { 656 String fieldName = e.getKey(); 657 Object expected = e.getValue(); 658 Object value = anonymousUserMap.get(fieldName); 659 if (value == null) { 660 if (expected != null) { 661 return false; 662 } 663 } else { 664 if (fulltext != null && fulltext.contains(fieldName)) { 665 if (!value.toString().toLowerCase().startsWith(expected.toString().toLowerCase())) { 666 return false; 667 } 668 } else { 669 if (!value.equals(expected)) { 670 return false; 671 } 672 } 673 } 674 } 675 return true; 676 } 677 678 @Override 679 public List<NuxeoPrincipal> searchPrincipals(String pattern) { 680 DocumentModelList entries = searchUsers(pattern); 681 List<NuxeoPrincipal> principals = new ArrayList<NuxeoPrincipal>(entries.size()); 682 for (DocumentModel entry : entries) { 683 principals.add(makePrincipal(entry)); 684 } 685 return principals; 686 } 687 688 @Override 689 public DocumentModelList searchGroups(String pattern) { 690 return searchGroups(pattern, null); 691 } 692 693 @Override 694 public String getUserSortField() { 695 return userSortField; 696 } 697 698 protected Map<String, String> getUserSortMap() { 699 return getDirectorySortMap(userSortField, userIdField); 700 } 701 702 protected Map<String, String> getGroupSortMap() { 703 return getDirectorySortMap(groupSortField, groupIdField); 704 } 705 706 protected Map<String, String> getDirectorySortMap(String descriptorSortField, String fallBackField) { 707 String sortField = descriptorSortField != null ? descriptorSortField : fallBackField; 708 Map<String, String> orderBy = new HashMap<String, String>(); 709 orderBy.put(sortField, DocumentModelComparator.ORDER_ASC); 710 return orderBy; 711 } 712 713 protected void notify(String userOrGroupName, String eventId) { 714 EventService eventService = Framework.getService(EventService.class); 715 eventService.sendEvent(new Event(USERMANAGER_TOPIC, eventId, this, userOrGroupName)); 716 } 717 718 /** 719 * Notifies user has changed so that the JaasCacheFlusher listener can make sure principals cache is reset. 720 */ 721 protected void notifyUserChanged(String userName) { 722 invalidatePrincipal(userName); 723 notify(userName, USERCHANGED_EVENT_ID); 724 } 725 726 protected void invalidatePrincipal(String userName) { 727 if (useCache()) { 728 principalCache.invalidate(userName); 729 } 730 } 731 732 /** 733 * Notifies group has changed so that the JaasCacheFlusher listener can make sure principals cache is reset. 734 */ 735 protected void notifyGroupChanged(String groupName) { 736 invalidateAllPrincipals(); 737 notify(groupName, GROUPCHANGED_EVENT_ID); 738 } 739 740 protected void invalidateAllPrincipals() { 741 if (useCache()) { 742 principalCache.invalidateAll(); 743 } 744 } 745 746 @Override 747 public Boolean areGroupsReadOnly() { 748 try (Session groupDir = dirService.open(groupDirectoryName)) { 749 return groupDir.isReadOnly(); 750 } catch (DirectoryException e) { 751 log.error(e); 752 return false; 753 } 754 } 755 756 @Override 757 public Boolean areUsersReadOnly() { 758 try (Session userDir = dirService.open(userDirectoryName)) { 759 return userDir.isReadOnly(); 760 } catch (DirectoryException e) { 761 log.error(e); 762 return false; 763 } 764 } 765 766 protected void checkGrouId(DocumentModel groupModel) { 767 // be sure the name does not contains trailing spaces 768 Object groupIdValue = groupModel.getProperty(groupSchemaName, groupIdField); 769 if (groupIdValue != null) { 770 groupModel.setProperty(groupSchemaName, groupIdField, groupIdValue.toString().trim()); 771 } 772 } 773 774 protected String getGroupId(DocumentModel groupModel) { 775 Object groupIdValue = groupModel.getProperty(groupSchemaName, groupIdField); 776 if (groupIdValue != null && !(groupIdValue instanceof String)) { 777 throw new NuxeoException("Invalid group id " + groupIdValue); 778 } 779 return (String) groupIdValue; 780 } 781 782 protected void checkUserId(DocumentModel userModel) { 783 Object userIdValue = userModel.getProperty(userSchemaName, userIdField); 784 if (userIdValue != null) { 785 userModel.setProperty(userSchemaName, userIdField, userIdValue.toString().trim()); 786 } 787 } 788 789 protected String getUserId(DocumentModel userModel) { 790 Object userIdValue = userModel.getProperty(userSchemaName, userIdField); 791 if (userIdValue != null && !(userIdValue instanceof String)) { 792 throw new NuxeoException("Invalid user id " + userIdValue); 793 } 794 return (String) userIdValue; 795 } 796 797 @Override 798 public DocumentModel createGroup(DocumentModel groupModel) { 799 return createGroup(groupModel, null); 800 } 801 802 @Override 803 public DocumentModel createUser(DocumentModel userModel) { 804 return createUser(userModel, null); 805 } 806 807 @Override 808 public void deleteGroup(String groupId) { 809 deleteGroup(groupId, null); 810 } 811 812 @Override 813 public void deleteGroup(DocumentModel groupModel) { 814 deleteGroup(groupModel, null); 815 } 816 817 @Override 818 public void deleteUser(String userId) { 819 deleteUser(userId, null); 820 } 821 822 @Override 823 public void deleteUser(DocumentModel userModel) { 824 String userId = getUserId(userModel); 825 deleteUser(userId); 826 } 827 828 @Override 829 public List<String> getGroupIds() { 830 try (Session groupDir = dirService.open(groupDirectoryName)) { 831 List<String> groupIds = groupDir.getProjection(Collections.<String, Serializable> emptyMap(), 832 groupDir.getIdField()); 833 Collections.sort(groupIds); 834 return groupIds; 835 } 836 } 837 838 @Override 839 public List<String> getUserIds() { 840 return getUserIds(null); 841 } 842 843 protected void removeVirtualFilters(Map<String, Serializable> filter) { 844 if (filter == null) { 845 return; 846 } 847 List<String> keys = new ArrayList<String>(filter.keySet()); 848 for (String key : keys) { 849 if (key.startsWith(VIRTUAL_FIELD_FILTER_PREFIX)) { 850 filter.remove(key); 851 } 852 } 853 } 854 855 @Override 856 public DocumentModelList searchGroups(Map<String, Serializable> filter, Set<String> fulltext) { 857 return searchGroups(filter, fulltext, null); 858 } 859 860 @Override 861 public DocumentModelList searchUsers(String pattern) { 862 return searchUsers(pattern, null); 863 } 864 865 @Override 866 public DocumentModelList searchUsers(Map<String, Serializable> filter, Set<String> fulltext) { 867 return searchUsers(filter, fulltext, getUserSortMap(), null); 868 } 869 870 @Override 871 public void updateGroup(DocumentModel groupModel) { 872 updateGroup(groupModel, null); 873 } 874 875 @Override 876 public void updateUser(DocumentModel userModel) { 877 updateUser(userModel, null); 878 } 879 880 @Override 881 public DocumentModel getBareGroupModel() { 882 String schema = dirService.getDirectorySchema(groupDirectoryName); 883 return BaseSession.createEntryModel(null, schema, null, null); 884 } 885 886 @Override 887 public void createGroup(NuxeoGroup group) { 888 DocumentModel newGroupModel = getBareGroupModel(); 889 newGroupModel.setProperty(groupSchemaName, groupIdField, group.getName()); 890 newGroupModel.setProperty(groupSchemaName, groupLabelField, group.getLabel()); 891 newGroupModel.setProperty(groupSchemaName, groupMembersField, group.getMemberUsers()); 892 newGroupModel.setProperty(groupSchemaName, groupSubGroupsField, group.getMemberGroups()); 893 createGroup(newGroupModel); 894 } 895 896 @Override 897 public void createPrincipal(NuxeoPrincipal principal) { 898 createUser(principal.getModel()); 899 } 900 901 @Override 902 public void deleteGroup(NuxeoGroup group) { 903 deleteGroup(group.getName()); 904 } 905 906 @Override 907 public void deletePrincipal(NuxeoPrincipal principal) { 908 deleteUser(principal.getName()); 909 } 910 911 @Override 912 public List<NuxeoGroup> getAvailableGroups() { 913 DocumentModelList groupModels = searchGroups(Collections.<String, Serializable> emptyMap(), null); 914 List<NuxeoGroup> groups = new ArrayList<NuxeoGroup>(groupModels.size()); 915 for (DocumentModel groupModel : groupModels) { 916 groups.add(makeGroup(groupModel)); 917 } 918 return groups; 919 } 920 921 @Override 922 public List<NuxeoPrincipal> getAvailablePrincipals() { 923 DocumentModelList userModels = searchUsers(Collections.<String, Serializable> emptyMap(), null); 924 List<NuxeoPrincipal> users = new ArrayList<NuxeoPrincipal>(userModels.size()); 925 for (DocumentModel userModel : userModels) { 926 users.add(makePrincipal(userModel)); 927 } 928 return users; 929 } 930 931 @Override 932 public DocumentModel getModelForUser(String name) { 933 return getUserModel(name); 934 } 935 936 @Override 937 public List<NuxeoPrincipal> searchByMap(Map<String, Serializable> filter, Set<String> pattern) { 938 try (Session userDir = dirService.open(userDirectoryName)) { 939 removeVirtualFilters(filter); 940 941 DocumentModelList entries = userDir.query(filter, pattern); 942 List<NuxeoPrincipal> principals = new ArrayList<NuxeoPrincipal>(entries.size()); 943 for (DocumentModel entry : entries) { 944 principals.add(makePrincipal(entry)); 945 } 946 if (isAnonymousMatching(filter, pattern)) { 947 principals.add(makeAnonymousPrincipal()); 948 } 949 return principals; 950 } 951 } 952 953 @Override 954 public void updateGroup(NuxeoGroup group) { 955 // XXX: need to refetch it for tests to pass, i don't get why (session 956 // id is used maybe?) 957 DocumentModel newGroupModel = getGroupModel(group.getName()); 958 newGroupModel.setProperty(groupSchemaName, groupIdField, group.getName()); 959 newGroupModel.setProperty(groupSchemaName, groupLabelField, group.getLabel()); 960 newGroupModel.setProperty(groupSchemaName, groupMembersField, group.getMemberUsers()); 961 newGroupModel.setProperty(groupSchemaName, groupSubGroupsField, group.getMemberGroups()); 962 updateGroup(newGroupModel); 963 } 964 965 @Override 966 public void updatePrincipal(NuxeoPrincipal principal) { 967 updateUser(principal.getModel()); 968 } 969 970 @Override 971 public List<String> getAdministratorsGroups() { 972 return administratorGroups; 973 } 974 975 protected List<String> getLeafPermissions(String perm) { 976 ArrayList<String> permissions = new ArrayList<String>(); 977 PermissionProvider permissionProvider = Framework.getService(PermissionProvider.class); 978 String[] subpermissions = permissionProvider.getSubPermissions(perm); 979 if (subpermissions == null || subpermissions.length <= 0) { 980 // it's a leaf 981 permissions.add(perm); 982 return permissions; 983 } 984 for (String subperm : subpermissions) { 985 permissions.addAll(getLeafPermissions(subperm)); 986 } 987 return permissions; 988 } 989 990 @Override 991 public String[] getUsersForPermission(String perm, ACP acp) { 992 return getUsersForPermission(perm, acp, null); 993 } 994 995 @Override 996 public Principal authenticate(String name, String password) { 997 return checkUsernamePassword(name, password) ? getPrincipal(name) : null; 998 } 999 1000 /*************** MULTI-TENANT-IMPLEMENTATION ************************/ 1001 1002 public DocumentModelList searchUsers(Map<String, Serializable> filter, Set<String> fulltext, 1003 Map<String, String> orderBy, DocumentModel context) { 1004 try (Session userDir = dirService.open(userDirectoryName, context)) { 1005 removeVirtualFilters(filter); 1006 1007 // XXX: do not fetch references, can be costly 1008 DocumentModelList entries = userDir.query(filter, fulltext, null, false); 1009 if (isAnonymousMatching(filter, fulltext)) { 1010 entries.add(makeVirtualUserEntry(getAnonymousUserId(), anonymousUser)); 1011 } 1012 1013 // TODO: match searchable virtual users 1014 1015 if (orderBy != null && !orderBy.isEmpty()) { 1016 // sort: cannot sort before virtual users are added 1017 Collections.sort(entries, new DocumentModelComparator(userSchemaName, orderBy)); 1018 } 1019 1020 return entries; 1021 } 1022 } 1023 1024 @Override 1025 public List<String> getUsersInGroup(String groupId, DocumentModel context) { 1026 String storeGroupId = multiTenantManagement.groupnameTranformer(this, groupId, context); 1027 return getGroup(storeGroupId).getMemberUsers(); 1028 } 1029 1030 @Override 1031 public DocumentModelList searchUsers(String pattern, DocumentModel context) { 1032 DocumentModelList entries = new DocumentModelListImpl(); 1033 if (pattern == null || pattern.length() == 0) { 1034 entries = searchUsers(Collections.<String, Serializable> emptyMap(), null); 1035 } else { 1036 pattern = pattern.trim(); 1037 Map<String, DocumentModel> uniqueEntries = new HashMap<String, DocumentModel>(); 1038 1039 for (Entry<String, MatchType> fieldEntry : userSearchFields.entrySet()) { 1040 Map<String, Serializable> filter = new HashMap<String, Serializable>(); 1041 filter.put(fieldEntry.getKey(), pattern); 1042 DocumentModelList fetchedEntries; 1043 if (fieldEntry.getValue() == MatchType.SUBSTRING) { 1044 fetchedEntries = searchUsers(filter, filter.keySet(), null, context); 1045 } else { 1046 fetchedEntries = searchUsers(filter, null, null, context); 1047 } 1048 for (DocumentModel entry : fetchedEntries) { 1049 uniqueEntries.put(entry.getId(), entry); 1050 } 1051 } 1052 log.debug(String.format("found %d unique entries", uniqueEntries.size())); 1053 entries.addAll(uniqueEntries.values()); 1054 } 1055 // sort 1056 Collections.sort(entries, new DocumentModelComparator(userSchemaName, getUserSortMap())); 1057 1058 return entries; 1059 } 1060 1061 @Override 1062 public DocumentModelList searchUsers(Map<String, Serializable> filter, Set<String> fulltext, DocumentModel context) { 1063 throw new UnsupportedOperationException(); 1064 } 1065 1066 @Override 1067 public List<String> getGroupIds(DocumentModel context) { 1068 throw new UnsupportedOperationException(); 1069 } 1070 1071 @Override 1072 public DocumentModelList searchGroups(Map<String, Serializable> filter, Set<String> fulltext, DocumentModel context) { 1073 filter = filter != null ? cloneMap(filter) : new HashMap<String, Serializable>(); 1074 HashSet<String> fulltextClone = fulltext != null ? cloneSet(fulltext) : new HashSet<String>(); 1075 multiTenantManagement.queryTransformer(this, filter, fulltextClone, context); 1076 1077 try (Session groupDir = dirService.open(groupDirectoryName, context)) { 1078 removeVirtualFilters(filter); 1079 return groupDir.query(filter, fulltextClone, getGroupSortMap(), false); 1080 } 1081 } 1082 1083 @Override 1084 public DocumentModel createGroup(DocumentModel groupModel, DocumentModel context) 1085 throws GroupAlreadyExistsException { 1086 groupModel = multiTenantManagement.groupTransformer(this, groupModel, context); 1087 1088 // be sure the name does not contains trailing spaces 1089 checkGrouId(groupModel); 1090 1091 try (Session groupDir = dirService.open(groupDirectoryName, context)) { 1092 String groupId = getGroupId(groupModel); 1093 1094 // check the group does not exist 1095 if (groupDir.hasEntry(groupId)) { 1096 throw new GroupAlreadyExistsException(); 1097 } 1098 groupModel = groupDir.createEntry(groupModel); 1099 notifyGroupChanged(groupId); 1100 notify(groupId, GROUPCREATED_EVENT_ID); 1101 return groupModel; 1102 1103 } 1104 } 1105 1106 @Override 1107 public DocumentModel getGroupModel(String groupIdValue, DocumentModel context) { 1108 String groupName = multiTenantManagement.groupnameTranformer(this, groupIdValue, context); 1109 if (groupName != null) { 1110 groupName = groupName.trim(); 1111 } 1112 1113 try (Session groupDir = dirService.open(groupDirectoryName, context)) { 1114 return groupDir.getEntry(groupName); 1115 } 1116 } 1117 1118 @Override 1119 public DocumentModel getUserModel(String userName, DocumentModel context) { 1120 if (userName == null) { 1121 return null; 1122 } 1123 1124 userName = userName.trim(); 1125 // return anonymous model 1126 if (anonymousUser != null && userName.equals(anonymousUser.getId())) { 1127 return makeVirtualUserEntry(getAnonymousUserId(), anonymousUser); 1128 } 1129 1130 try (Session userDir = dirService.open(userDirectoryName, context)) { 1131 return userDir.getEntry(userName); 1132 } 1133 } 1134 1135 protected Map<String, Serializable> cloneMap(Map<String, Serializable> map) { 1136 Map<String, Serializable> result = new HashMap<String, Serializable>(); 1137 for (String key : map.keySet()) { 1138 result.put(key, map.get(key)); 1139 } 1140 return result; 1141 } 1142 1143 protected HashSet<String> cloneSet(Set<String> set) { 1144 HashSet<String> result = new HashSet<String>(); 1145 for (String key : set) { 1146 result.add(key); 1147 } 1148 return result; 1149 } 1150 1151 @Override 1152 public NuxeoPrincipal getPrincipal(String username, DocumentModel context) { 1153 if (username == null) { 1154 return null; 1155 } 1156 String anonymousUserId = getAnonymousUserId(); 1157 if (username.equals(anonymousUserId)) { 1158 return makeAnonymousPrincipal(); 1159 } 1160 if (virtualUsers.containsKey(username)) { 1161 return makeVirtualPrincipal(virtualUsers.get(username)); 1162 } 1163 DocumentModel userModel = getUserModel(username, context); 1164 if (userModel != null) { 1165 return makePrincipal(userModel); 1166 } 1167 return null; 1168 } 1169 1170 @Override 1171 public DocumentModelList searchGroups(String pattern, DocumentModel context) { 1172 DocumentModelList entries = new DocumentModelListImpl(); 1173 if (pattern == null || pattern.length() == 0) { 1174 entries = searchGroups(Collections.<String, Serializable> emptyMap(), null); 1175 } else { 1176 pattern = pattern.trim(); 1177 Map<String, DocumentModel> uniqueEntries = new HashMap<String, DocumentModel>(); 1178 1179 for (Entry<String, MatchType> fieldEntry : groupSearchFields.entrySet()) { 1180 Map<String, Serializable> filter = new HashMap<String, Serializable>(); 1181 filter.put(fieldEntry.getKey(), pattern); 1182 DocumentModelList fetchedEntries; 1183 if (fieldEntry.getValue() == MatchType.SUBSTRING) { 1184 fetchedEntries = searchGroups(filter, filter.keySet(), context); 1185 } else { 1186 fetchedEntries = searchGroups(filter, null, context); 1187 } 1188 for (DocumentModel entry : fetchedEntries) { 1189 uniqueEntries.put(entry.getId(), entry); 1190 } 1191 } 1192 log.debug(String.format("found %d unique group entries", uniqueEntries.size())); 1193 entries.addAll(uniqueEntries.values()); 1194 } 1195 // sort 1196 Collections.sort(entries, new DocumentModelComparator(groupSchemaName, getGroupSortMap())); 1197 1198 return entries; 1199 } 1200 1201 @Override 1202 public List<String> getUserIds(DocumentModel context) { 1203 try (Session userDir = dirService.open(userDirectoryName, context)) { 1204 List<String> userIds = userDir.getProjection(Collections.<String, Serializable> emptyMap(), 1205 userDir.getIdField()); 1206 Collections.sort(userIds); 1207 return userIds; 1208 } 1209 } 1210 1211 @Override 1212 public DocumentModel createUser(DocumentModel userModel, DocumentModel context) throws UserAlreadyExistsException { 1213 // be sure UserId does not contains any trailing spaces 1214 checkUserId(userModel); 1215 1216 try (Session userDir = dirService.open(userDirectoryName, context)) { 1217 String userId = getUserId(userModel); 1218 1219 // check the user does not exist 1220 if (userDir.hasEntry(userId)) { 1221 throw new UserAlreadyExistsException(); 1222 } 1223 1224 String schema = dirService.getDirectorySchema(userDirectoryName); 1225 String clearUsername = (String) userModel.getProperty(schema, userDir.getIdField()); 1226 String clearPassword = (String) userModel.getProperty(schema, userDir.getPasswordField()); 1227 1228 userModel = userDir.createEntry(userModel); 1229 1230 syncDigestAuthPassword(clearUsername, clearPassword); 1231 1232 notifyUserChanged(userId); 1233 notify(userId, USERCREATED_EVENT_ID); 1234 return userModel; 1235 1236 } 1237 } 1238 1239 @Override 1240 public void updateUser(DocumentModel userModel, DocumentModel context) { 1241 try (Session userDir = dirService.open(userDirectoryName, context)) { 1242 String userId = getUserId(userModel); 1243 1244 if (!userDir.hasEntry(userId)) { 1245 throw new DirectoryException("user does not exist: " + userId); 1246 } 1247 1248 String schema = dirService.getDirectorySchema(userDirectoryName); 1249 String clearUsername = (String) userModel.getProperty(schema, userDir.getIdField()); 1250 String clearPassword = (String) userModel.getProperty(schema, userDir.getPasswordField()); 1251 1252 userDir.updateEntry(userModel); 1253 1254 syncDigestAuthPassword(clearUsername, clearPassword); 1255 1256 notifyUserChanged(userId); 1257 notify(userId, USERMODIFIED_EVENT_ID); 1258 } 1259 } 1260 1261 @Override 1262 public void deleteUser(DocumentModel userModel, DocumentModel context) { 1263 String userId = getUserId(userModel); 1264 deleteUser(userId, context); 1265 } 1266 1267 @Override 1268 public void deleteUser(String userId, DocumentModel context) { 1269 try (Session userDir = dirService.open(userDirectoryName, context)) { 1270 if (!userDir.hasEntry(userId)) { 1271 throw new DirectoryException("User does not exist: " + userId); 1272 } 1273 userDir.deleteEntry(userId); 1274 notifyUserChanged(userId); 1275 notify(userId, USERDELETED_EVENT_ID); 1276 1277 } finally { 1278 notifyUserChanged(userId); 1279 } 1280 } 1281 1282 @Override 1283 public void updateGroup(DocumentModel groupModel, DocumentModel context) { 1284 try (Session groupDir = dirService.open(groupDirectoryName, context)) { 1285 String groupId = getGroupId(groupModel); 1286 1287 if (!groupDir.hasEntry(groupId)) { 1288 throw new DirectoryException("group does not exist: " + groupId); 1289 } 1290 groupDir.updateEntry(groupModel); 1291 notifyGroupChanged(groupId); 1292 notify(groupId, GROUPMODIFIED_EVENT_ID); 1293 } 1294 } 1295 1296 @Override 1297 public void deleteGroup(DocumentModel groupModel, DocumentModel context) { 1298 String groupId = getGroupId(groupModel); 1299 deleteGroup(groupId, context); 1300 } 1301 1302 @Override 1303 public void deleteGroup(String groupId, DocumentModel context) { 1304 try (Session groupDir = dirService.open(groupDirectoryName, context)) { 1305 if (!groupDir.hasEntry(groupId)) { 1306 throw new DirectoryException("Group does not exist: " + groupId); 1307 } 1308 groupDir.deleteEntry(groupId); 1309 notifyGroupChanged(groupId); 1310 notify(groupId, GROUPDELETED_EVENT_ID); 1311 } 1312 } 1313 1314 @Override 1315 public List<String> getGroupsInGroup(String parentId, DocumentModel context) { 1316 return getGroup(parentId, null).getMemberGroups(); 1317 } 1318 1319 @Override 1320 public List<String> getTopLevelGroups(DocumentModel context) { 1321 try (Session groupDir = dirService.open(groupDirectoryName, context)) { 1322 List<String> topLevelGroups = new LinkedList<String>(); 1323 // XXX retrieve all entries with references, can be costly. 1324 DocumentModelList groups = groupDir.query(Collections.<String, Serializable> emptyMap(), null, null, true); 1325 for (DocumentModel group : groups) { 1326 @SuppressWarnings("unchecked") 1327 List<String> parents = (List<String>) group.getProperty(groupSchemaName, groupParentGroupsField); 1328 1329 if (parents == null || parents.isEmpty()) { 1330 topLevelGroups.add(group.getId()); 1331 } 1332 } 1333 return topLevelGroups; 1334 } 1335 } 1336 1337 @Override 1338 public List<String> getUsersInGroupAndSubGroups(String groupId, DocumentModel context) { 1339 Set<String> groups = new HashSet<String>(); 1340 groups.add(groupId); 1341 appendSubgroups(groupId, groups, context); 1342 1343 Set<String> users = new HashSet<String>(); 1344 for (String groupid : groups) { 1345 users.addAll(getGroup(groupid, context).getMemberUsers()); 1346 } 1347 1348 return new ArrayList<String>(users); 1349 } 1350 1351 @Override 1352 public String[] getUsersForPermission(String perm, ACP acp, DocumentModel context) { 1353 PermissionProvider permissionProvider = Framework.getService(PermissionProvider.class); 1354 // using a hashset to avoid duplicates 1355 HashSet<String> usernames = new HashSet<String>(); 1356 1357 ACL merged = acp.getMergedACLs("merged"); 1358 // The list of permission that is has "perm" as its (compound) 1359 // permission 1360 ArrayList<ACE> filteredACEbyPerm = new ArrayList<ACE>(); 1361 1362 List<String> currentPermissions = getLeafPermissions(perm); 1363 1364 for (ACE ace : merged.getACEs()) { 1365 // Checking if the permission contains the permission we want to 1366 // check (we use the security service method for coumpound 1367 // permissions) 1368 List<String> acePermissions = getLeafPermissions(ace.getPermission()); 1369 1370 // Everything is a special permission (not compound) 1371 if (SecurityConstants.EVERYTHING.equals(ace.getPermission())) { 1372 acePermissions = Arrays.asList(permissionProvider.getPermissions()); 1373 } 1374 1375 if (acePermissions.containsAll(currentPermissions)) { 1376 // special case: everybody perm grant false, don't take in 1377 // account the previous ace 1378 if (SecurityConstants.EVERYONE.equals(ace.getUsername()) && !ace.isGranted()) { 1379 break; 1380 } 1381 filteredACEbyPerm.add(ace); 1382 } 1383 } 1384 1385 for (ACE ace : filteredACEbyPerm) { 1386 String aceUsername = ace.getUsername(); 1387 List<String> users = null; 1388 // If everyone, add/remove all the users 1389 if (SecurityConstants.EVERYONE.equals(aceUsername)) { 1390 users = getUserIds(); 1391 } 1392 // if a group, add/remove all the user from the group (and 1393 // subgroups) 1394 if (users == null) { 1395 NuxeoGroup group; 1396 group = getGroup(aceUsername, context); 1397 if (group != null) { 1398 users = getUsersInGroupAndSubGroups(aceUsername, context); 1399 } 1400 1401 } 1402 // otherwise, add the user 1403 if (users == null) { 1404 users = new ArrayList<String>(); 1405 users.add(aceUsername); 1406 } 1407 if (ace.isGranted()) { 1408 usernames.addAll(users); 1409 } else { 1410 usernames.removeAll(users); 1411 } 1412 } 1413 return usernames.toArray(new String[usernames.size()]); 1414 } 1415 1416 @Override 1417 public boolean aboutToHandleEvent(Event event) { 1418 return true; 1419 } 1420 1421 @Override 1422 public void handleEvent(Event event) { 1423 String id = event.getId(); 1424 if (INVALIDATE_PRINCIPAL_EVENT_ID.equals(id)) { 1425 invalidatePrincipal((String) event.getData()); 1426 } else if (INVALIDATE_ALL_PRINCIPALS_EVENT_ID.equals(id)) { 1427 invalidateAllPrincipals(); 1428 } 1429 } 1430 1431}