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