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