/**
   * We need to order the UserManagers the same way as the Realms are ordered. We need to be able to
   * find a user based on the ID.
   *
   * <p>This my never go away, but the current reason why we need it is:
   * https://issues.apache.org/jira/browse/KI-77 There is no (clean) way to resolve a realms roles
   * into permissions. take a look at the issue and VOTE!
   *
   * @return the list of UserManagers in the order (as close as possible) to the list of realms.
   */
  private List<UserManager> orderUserManagers() {
    List<UserManager> orderedLocators = new ArrayList<>();

    List<UserManager> unOrderdLocators = new ArrayList<>(getUserManagers());

    Map<String, UserManager> realmToUserManagerMap = new HashMap<>();

    for (UserManager userManager : getUserManagers()) {
      if (userManager.getAuthenticationRealmName() != null) {
        realmToUserManagerMap.put(userManager.getAuthenticationRealmName(), userManager);
      }
    }

    // get the sorted order of realms from the realm locator
    Collection<Realm> realms = realmSecurityManager.getRealms();

    for (Realm realm : realms) {
      // now user the realm.name to find the UserManager
      if (realmToUserManagerMap.containsKey(realm.getName())) {
        UserManager userManager = realmToUserManagerMap.get(realm.getName());
        // remove from unorderd and add to orderd
        unOrderdLocators.remove(userManager);
        orderedLocators.add(userManager);
      }
    }

    // now add all the un-ordered ones to the ordered ones, this way they will be at the end of the
    // ordered list
    orderedLocators.addAll(unOrderdLocators);

    return orderedLocators;
  }
  @Override
  public void setUsersRoles(String userId, String source, Set<RoleIdentifier> roleIdentifiers)
      throws UserNotFoundException {
    // TODO: this is a bit sticky, what we really want to do is just expose the
    // RoleMappingUserManagers this way (i
    // think), maybe this is too generic

    boolean foundUser = false;

    for (UserManager userManager : getUserManagers()) {
      if (RoleMappingUserManager.class.isInstance(userManager)) {
        RoleMappingUserManager roleMappingUserManager = (RoleMappingUserManager) userManager;
        try {
          foundUser = true;
          roleMappingUserManager.setUsersRoles(
              userId,
              source,
              RoleIdentifier.getRoleIdentifiersForSource(userManager.getSource(), roleIdentifiers));
        } catch (UserNotFoundException e) {
          log.debug(
              "User '{}' is not managed by the user-manager: {}", userId, userManager.getSource());
        }
      }
    }

    if (!foundUser) {
      throw new UserNotFoundException(userId);
    }
    // clear the authz realm caches
    eventBus.post(new AuthorizationConfigurationChanged());
  }
  @Override
  public Set<User> searchUsers(UserSearchCriteria criteria) {
    Set<User> result = new HashSet<>();

    // if the source is not set search all realms.
    if (Strings2.isBlank(criteria.getSource())) {
      // search all user managers
      for (UserManager userManager : getUserManagers()) {
        Set<User> users = userManager.searchUsers(criteria);
        if (users != null) {
          result.addAll(users);
        }
      }
    } else {
      try {
        result.addAll(getUserManager(criteria.getSource()).searchUsers(criteria));
      } catch (NoSuchUserManagerException e) {
        log.warn("UserManager: {} was not found.", criteria.getSource(), e);
      }
    }

    // now add all the roles to the users
    for (User user : result) {
      // add roles from other user managers
      addOtherRolesToUser(user);
    }

    return result;
  }
  @Override
  public Set<User> listUsers() {
    Set<User> result = new HashSet<>();

    for (UserManager userManager : getUserManagers()) {
      result.addAll(userManager.listUsers());
    }

    // now add all the roles to the users
    for (User user : result) {
      // add roles from other user managers
      addOtherRolesToUser(user);
    }

    return result;
  }
  @Override
  public void changePassword(String userId, String newPassword) throws UserNotFoundException {
    User user = getUser(userId);

    try {
      UserManager userManager = getUserManager(user.getSource());
      userManager.changePassword(userId, newPassword);
    } catch (NoSuchUserManagerException e) {
      // this should NEVER happen
      log.warn(
          "User '{}' with source: '{}' but could not find the user-manager for that source.",
          userId,
          user.getSource());
    }

    // flush authc
    eventBus.post(new UserPrincipalsExpired(userId, user.getSource()));
  }
  @Override
  public void deleteUser(String userId, String source)
      throws UserNotFoundException, NoSuchUserManagerException {
    checkNotNull(userId, "User ID may not be null");

    Subject subject = getSubject();
    if (subject.getPrincipal() != null && userId.equals(subject.getPrincipal().toString())) {
      throw new IllegalArgumentException("Can not delete currently signed in user");
    }

    AnonymousConfiguration anonymousConfiguration = anonymousManager.getConfiguration();
    if (anonymousConfiguration.isEnabled() && userId.equals(anonymousConfiguration.getUserId())) {
      throw new IllegalArgumentException("Can not delete anonymous user");
    }

    UserManager userManager = getUserManager(source);
    userManager.deleteUser(userId);

    // flush authc
    eventBus.post(new UserPrincipalsExpired(userId, source));
  }
 private void addOtherRolesToUser(final User user) {
   // then save the users Roles
   for (UserManager userManager : getUserManagers()) {
     // skip the user manager that owns the user, we already did that
     // these user managers will only have roles
     if (!userManager.getSource().equals(user.getSource())
         && RoleMappingUserManager.class.isInstance(userManager)) {
       try {
         RoleMappingUserManager roleMappingUserManager = (RoleMappingUserManager) userManager;
         Set<RoleIdentifier> roleIdentifiers =
             roleMappingUserManager.getUsersRoles(user.getUserId(), user.getSource());
         if (roleIdentifiers != null) {
           user.addAllRoles(roleIdentifiers);
         }
       } catch (UserNotFoundException e) {
         log.debug(
             "User '{}' is not managed by the user-manager: {}",
             user.getUserId(),
             userManager.getSource());
       }
     }
   }
 }
  private User findUser(String userId, UserManager userManager) throws UserNotFoundException {
    log.trace("Finding user: {} in user-manager: {}", userId, userManager);

    User user = userManager.getUser(userId);
    if (user == null) {
      throw new UserNotFoundException(userId);
    }
    log.trace("Found user: {}", user);

    // add roles from other user managers
    addOtherRolesToUser(user);

    return user;
  }
  @Override
  public User updateUser(User user) throws UserNotFoundException, NoSuchUserManagerException {
    // first update the user
    // this is the UserManager that owns the user
    UserManager userManager = getUserManager(user.getSource());

    if (!userManager.supportsWrite()) {
      throw new ConfigurationException(
          "UserManager: " + userManager.getSource() + " does not support writing.");
    }

    final User oldUser = userManager.getUser(user.getUserId());
    userManager.updateUser(user);
    if (oldUser.getStatus() == UserStatus.active && user.getStatus() != oldUser.getStatus()) {
      // clear the realm authc caches as user got disabled
      eventBus.post(new UserPrincipalsExpired(user.getUserId(), user.getSource()));
    }

    // then save the users Roles
    for (UserManager tmpUserManager : getUserManagers()) {
      // skip the user manager that owns the user, we already did that
      // these user managers will only save roles
      if (!tmpUserManager.getSource().equals(user.getSource())
          && RoleMappingUserManager.class.isInstance(tmpUserManager)) {
        try {
          RoleMappingUserManager roleMappingUserManager = (RoleMappingUserManager) tmpUserManager;
          roleMappingUserManager.setUsersRoles(
              user.getUserId(),
              user.getSource(),
              RoleIdentifier.getRoleIdentifiersForSource(user.getSource(), user.getRoles()));
        } catch (UserNotFoundException e) {
          log.debug(
              "User '{}' is not managed by the user-manager: {}",
              user.getUserId(),
              tmpUserManager.getSource());
        }
      }
    }

    // clear the realm authz caches as user might get roles changed
    eventBus.post(new AuthorizationConfigurationChanged());

    return user;
  }
  @Override
  public User addUser(User user, String password) throws NoSuchUserManagerException {
    // first save the user
    // this is the UserManager that owns the user
    UserManager userManager = getUserManager(user.getSource());

    if (!userManager.supportsWrite()) {
      throw new ConfigurationException(
          "UserManager: " + userManager.getSource() + " does not support writing.");
    }

    userManager.addUser(user, password);

    // then save the users Roles
    for (UserManager tmpUserManager : getUserManagers()) {
      // skip the user manager that owns the user, we already did that
      // these user managers will only save roles
      if (!tmpUserManager.getSource().equals(user.getSource())
          && RoleMappingUserManager.class.isInstance(tmpUserManager)) {
        try {
          RoleMappingUserManager roleMappingUserManager = (RoleMappingUserManager) tmpUserManager;
          roleMappingUserManager.setUsersRoles(
              user.getUserId(),
              user.getSource(),
              RoleIdentifier.getRoleIdentifiersForSource(user.getSource(), user.getRoles()));
        } catch (UserNotFoundException e) {
          log.debug(
              "User '{}' is not managed by the user-manager: {}",
              user.getUserId(),
              tmpUserManager.getSource());
        }
      }
    }

    return user;
  }