All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.usergrid.management.cassandra.ManagementServiceImpl Maven / Gradle / Ivy

There is a newer version: 0.0.27.1
Show newest version
/*******************************************************************************
 * Copyright 2012 Apigee Corporation
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.usergrid.management.cassandra;

import static java.lang.Boolean.parseBoolean;
import static org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString;
import static org.apache.commons.codec.digest.DigestUtils.sha;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.usergrid.locking.LockHelper.getUniqueUpdateLock;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_ACTIVATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_CONFIRMATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_RESETPW_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_CONFIRMED_AWAITING_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_INVITED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_PASSWORD_RESET;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_USER_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_FOOTER;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_CONFIRMED_AWAITING_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_ACTIVATED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_CONFIRMATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_CONFIRMED_AWAITING_ACTIVATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_PASSWORD_RESET;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_PIN_REQUEST;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_MAILER_EMAIL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_ORGANIZATION_ACTIVATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SETUP_TEST_ACCOUNT;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_EMAIL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_ALLOWED;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_EMAIL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_NAME;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_PASSWORD;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_EMAIL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_NAME;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_PASSWORD;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_USERNAME;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_APP;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ORGANIZATION;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_USER_ACTIVATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_USER_CONFIRMATION_URL;
import static org.usergrid.management.AccountCreationProps.PROPERTIES_USER_RESETPW_URL;
import static org.usergrid.persistence.CredentialsInfo.getCredentialsSecret;
import static org.usergrid.persistence.Schema.*;
import static org.usergrid.persistence.cassandra.CassandraService.MANAGEMENT_APPLICATION_ID;
import static org.usergrid.persistence.entities.Activity.PROPERTY_ACTOR;
import static org.usergrid.persistence.entities.Activity.PROPERTY_ACTOR_NAME;
import static org.usergrid.persistence.entities.Activity.PROPERTY_CATEGORY;
import static org.usergrid.persistence.entities.Activity.PROPERTY_CONTENT;
import static org.usergrid.persistence.entities.Activity.PROPERTY_DISPLAY_NAME;
import static org.usergrid.persistence.entities.Activity.PROPERTY_ENTITY_TYPE;
import static org.usergrid.persistence.entities.Activity.PROPERTY_OBJECT;
import static org.usergrid.persistence.entities.Activity.PROPERTY_OBJECT_ENTITY_TYPE;
import static org.usergrid.persistence.entities.Activity.PROPERTY_OBJECT_NAME;
import static org.usergrid.persistence.entities.Activity.PROPERTY_OBJECT_TYPE;
import static org.usergrid.persistence.entities.Activity.PROPERTY_TITLE;
import static org.usergrid.persistence.entities.Activity.PROPERTY_VERB;
import static org.usergrid.security.AuthPrincipalType.ADMIN_USER;
import static org.usergrid.security.AuthPrincipalType.APPLICATION;
import static org.usergrid.security.AuthPrincipalType.APPLICATION_USER;
import static org.usergrid.security.AuthPrincipalType.ORGANIZATION;
import static org.usergrid.security.oauth.ClientCredentialsInfo.getTypeFromClientId;
import static org.usergrid.security.oauth.ClientCredentialsInfo.getUUIDFromClientId;
import static org.usergrid.security.tokens.TokenCategory.ACCESS;
import static org.usergrid.security.tokens.TokenCategory.EMAIL;
import static org.usergrid.services.ServiceParameter.parameters;
import static org.usergrid.services.ServicePayload.payload;
import static org.usergrid.services.ServiceResults.genericServiceResults;
import static org.usergrid.utils.ClassUtils.cast;
import static org.usergrid.utils.ConversionUtils.bytes;
import static org.usergrid.utils.ConversionUtils.uuid;
import static org.usergrid.utils.ListUtils.anyNull;
import static org.usergrid.utils.MapUtils.hashMap;
import static org.usergrid.utils.PasswordUtils.mongoPassword;

import java.nio.ByteBuffer;
import java.util.*;
import java.util.Map.Entry;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.usergrid.locking.Lock;
import org.usergrid.locking.LockManager;
import org.usergrid.management.AccountCreationProps;
import org.usergrid.management.ActivationState;
import org.usergrid.management.ApplicationInfo;
import org.usergrid.management.ManagementService;
import org.usergrid.management.OrganizationInfo;
import org.usergrid.management.OrganizationOwnerInfo;
import org.usergrid.management.UserInfo;
import org.usergrid.management.exceptions.*;
import org.usergrid.persistence.CredentialsInfo;
import org.usergrid.persistence.Entity;
import org.usergrid.persistence.EntityManager;
import org.usergrid.persistence.EntityManagerFactory;
import org.usergrid.persistence.EntityRef;
import org.usergrid.persistence.Identifier;
import org.usergrid.persistence.Results;
import org.usergrid.persistence.Results.Level;
import org.usergrid.persistence.SimpleEntityRef;
import org.usergrid.persistence.entities.Application;
import org.usergrid.persistence.entities.Group;
import org.usergrid.persistence.entities.User;
import org.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException;
import org.usergrid.persistence.exceptions.EntityNotFoundException;
import org.usergrid.security.AuthPrincipalInfo;
import org.usergrid.security.AuthPrincipalType;
import org.usergrid.security.crypto.EncryptionService;
import org.usergrid.security.oauth.AccessInfo;
import org.usergrid.security.oauth.ClientCredentialsInfo;
import org.usergrid.security.salt.SaltProvider;
import org.usergrid.security.shiro.PrincipalCredentialsToken;
import org.usergrid.security.shiro.credentials.ApplicationClientCredentials;
import org.usergrid.security.shiro.credentials.OrganizationClientCredentials;
import org.usergrid.security.shiro.principals.ApplicationPrincipal;
import org.usergrid.security.shiro.principals.OrganizationPrincipal;
import org.usergrid.security.shiro.utils.SubjectUtils;
import org.usergrid.security.tokens.TokenCategory;
import org.usergrid.security.tokens.TokenInfo;
import org.usergrid.security.tokens.TokenService;
import org.usergrid.security.tokens.exceptions.TokenException;
import org.usergrid.services.ServiceAction;
import org.usergrid.services.ServiceManager;
import org.usergrid.services.ServiceManagerFactory;
import org.usergrid.services.ServiceRequest;
import org.usergrid.services.ServiceResults;
import org.usergrid.utils.*;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public class ManagementServiceImpl implements ManagementService {

  /**
   * Key for the user's pin
   */
  protected static final String USER_PIN = "pin";

  /**
   * Key for the user's oauth secret
   */
  protected static final String USER_TOKEN = "secret";

  /**
   * Key for the user's mongo password
   */
  protected static final String USER_MONGO_PASSWORD = "mongo_pwd";

  /**
   * Key for the user's password
   */
  protected static final String USER_PASSWORD = "password";

  protected static final String USER_PASSWORD_HISTORY = "password_history";

  private static final String TOKEN_TYPE_ACTIVATION = "activate";

  private static final String TOKEN_TYPE_PASSWORD_RESET = "resetpw";

  private static final String TOKEN_TYPE_CONFIRM = "confirm";

  public static final String MANAGEMENT_APPLICATION = "management";

  public static final String APPLICATION_INFO = "application_info";

  private static final Logger logger = LoggerFactory.getLogger(ManagementServiceImpl.class);

  public static final String OAUTH_SECRET_SALT = "super secret oauth value";

  private static final String ORGANIZATION_PROPERTIES_DICTIONARY = "orgProperties";

  protected ServiceManagerFactory smf;

  protected EntityManagerFactory emf;

  protected AccountCreationPropsImpl properties;

  protected LockManager lockManager;

  protected TokenService tokens;

  protected SaltProvider saltProvider;

  @Autowired
  protected MailUtils mailUtils;

  protected EncryptionService encryptionService;

  /**
   * Must be constructed with a CassandraClientPool.
   * 
   */
  public ManagementServiceImpl() {
  }

  @Autowired
  public void setEntityManagerFactory(EntityManagerFactory emf) {
    logger.info("ManagementServiceImpl.setEntityManagerFactory");
    this.emf = emf;
  }

  @Autowired
  public void setProperties(Properties properties) {
    this.properties = new AccountCreationPropsImpl(properties);
  }

  @Autowired
  public void setTokenService(TokenService tokens) {
    this.tokens = tokens;
  }

  @Autowired
  public void setServiceManagerFactory(ServiceManagerFactory smf) {
    this.smf = smf;
  }

  public LockManager getLockManager() {
    return lockManager;
  }

  @Autowired
  public void setLockManager(LockManager lockManager) {
    this.lockManager = lockManager;
  }

  /**
   * @param encryptionService
   *          the encryptionService to set
   */
  @Autowired
  public void setEncryptionService(EncryptionService encryptionService) {
    this.encryptionService = encryptionService;
  }

  @Override
  public void setup() throws Exception {

    if (parseBoolean(properties.getProperty(PROPERTIES_SETUP_TEST_ACCOUNT))) {
      String test_app_name = properties.getProperty(PROPERTIES_TEST_ACCOUNT_APP);
      String test_organization_name = properties.getProperty(PROPERTIES_TEST_ACCOUNT_ORGANIZATION);
      String test_admin_username = properties.getProperty(PROPERTIES_TEST_ACCOUNT_ADMIN_USER_USERNAME);
      String test_admin_name = properties.getProperty(PROPERTIES_TEST_ACCOUNT_ADMIN_USER_NAME);
      String test_admin_email = properties.getProperty(PROPERTIES_TEST_ACCOUNT_ADMIN_USER_EMAIL);
      String test_admin_password = properties.getProperty(PROPERTIES_TEST_ACCOUNT_ADMIN_USER_PASSWORD);

      if (anyNull(test_app_name, test_organization_name, test_admin_username, test_admin_name, test_admin_email,
          test_admin_password)) {
        logger.warn("Missing values for test app, check properties.  Skipping test app setup...");
        return;
      }

      OrganizationInfo organization = getOrganizationByName(test_organization_name);

      if (organization == null) {
        OrganizationOwnerInfo created = createOwnerAndOrganization(test_organization_name, test_admin_username,
            test_admin_name, test_admin_email, test_admin_password, true, false);
        organization = created.getOrganization();
      }

      if (!getApplicationsForOrganization(organization.getUuid()).containsValue(test_app_name)) {
        createApplication(organization.getUuid(), test_app_name);
      }

    } else {
      logger.warn("Test app creation disabled");
    }

    if (superuserEnabled()) {
      provisionSuperuser();
    }

  }

  public boolean superuserEnabled() {
    boolean superuser_enabled = parseBoolean(properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_ALLOWED));
    String superuser_username = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_NAME);
    String superuser_email = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_EMAIL);
    String superuser_password = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_PASSWORD);

    return superuser_enabled && !anyNull(superuser_username, superuser_email, superuser_password);
  }

  @Override
  public void provisionSuperuser() throws Exception {
    boolean superuser_enabled = parseBoolean(properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_ALLOWED));
    String superuser_username = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_NAME);
    String superuser_email = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_EMAIL);
    String superuser_password = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_PASSWORD);

    if (!anyNull(superuser_username, superuser_email, superuser_password)) {
      UserInfo user = this.getAdminUserByUsername(superuser_username);
      if (user == null) {
        createAdminUser(superuser_username, "Super User", superuser_email, superuser_password, superuser_enabled,
            !superuser_enabled);
      } else {
        this.setAdminUserPassword(user.getUuid(), superuser_password);
      }
    } else {
      logger.warn("Missing values for superuser account, check properties.  Skipping superuser account setup...");
    }
  }

  public String generateOAuthSecretKey(AuthPrincipalType type) {
    long timestamp = System.currentTimeMillis();
    ByteBuffer bytes = ByteBuffer.allocate(20);
    bytes.put(sha(timestamp + OAUTH_SECRET_SALT + UUID.randomUUID()));
    String secret = type.getBase64Prefix() + encodeBase64URLSafeString(bytes.array());
    return secret;
  }

  @SuppressWarnings("serial")
  @Override
  public void postOrganizationActivity(UUID organizationId, final UserInfo user, String verb, final EntityRef object,
      final String objectType, final String objectName, String title, String content) throws Exception {
    ServiceManager sm = smf.getServiceManager(MANAGEMENT_APPLICATION_ID);

    Map properties = new HashMap();
    properties.put(PROPERTY_VERB, verb);
    properties.put(PROPERTY_CATEGORY, "admin");
    if (content != null) {
      properties.put(PROPERTY_CONTENT, content);
    }
    if (title != null) {
      properties.put(PROPERTY_TITLE, title);
    }
    properties.put(PROPERTY_ACTOR, new HashMap() {
      {
        put(PROPERTY_DISPLAY_NAME, user.getName());
        put(PROPERTY_OBJECT_TYPE, "person");
        put(PROPERTY_ENTITY_TYPE, "user");
        put(PROPERTY_UUID, user.getUuid());
      }
    });
    properties.put(PROPERTY_OBJECT, new HashMap() {
      {
        put(PROPERTY_DISPLAY_NAME, objectName);
        put(PROPERTY_OBJECT_TYPE, objectType);
        put(PROPERTY_ENTITY_TYPE, object.getType());
        put(PROPERTY_UUID, object.getUuid());
      }
    });

    sm.newRequest(ServiceAction.POST, parameters("groups", organizationId, "activities"), payload(properties))
        .execute().getEntity();

  }

  @Override
  public ServiceResults getOrganizationActivity(OrganizationInfo organization) throws Exception {
    ServiceManager sm = smf.getServiceManager(MANAGEMENT_APPLICATION_ID);
    return sm.newRequest(ServiceAction.GET, parameters("groups", organization.getUuid(), "feed")).execute();
  }

  @Override
  public ServiceResults getOrganizationActivityForAdminUser(OrganizationInfo organization, UserInfo user)
      throws Exception {
    ServiceManager sm = smf.getServiceManager(MANAGEMENT_APPLICATION_ID);
    return sm.newRequest(ServiceAction.GET,
        parameters("groups", organization.getUuid(), "users", user.getUuid(), "feed")).execute();
  }

  @Override
  public ServiceResults getAdminUserActivity(UserInfo user) throws Exception {
    ServiceManager sm = smf.getServiceManager(MANAGEMENT_APPLICATION_ID);
    return sm.newRequest(ServiceAction.GET, parameters("users", user.getUuid(), "feed")).execute();
  }

  @Override
  public OrganizationOwnerInfo createOwnerAndOrganization(String organizationName, String username, String name,
      String email, String password) throws Exception {

    boolean activated = !newAdminUsersNeedSysAdminApproval() && !newOrganizationsNeedSysAdminApproval();
    boolean disabled = newAdminUsersRequireConfirmation();
    // if we are active and enabled, skip the send email step

    return createOwnerAndOrganization(organizationName, username, name, email, password, activated, disabled, null, null);

  }

  @Override
  public OrganizationOwnerInfo createOwnerAndOrganization(String organizationName, String username, String name,
      String email, String password, boolean activated, boolean disabled) throws Exception {
    return createOwnerAndOrganization(organizationName, username, name, email, password, activated, disabled, null, null);
  }

  @Override
  public OrganizationOwnerInfo createOwnerAndOrganization(String organizationName, String username, String name,
      String email, String password, boolean activated, boolean disabled, Map userProperties,
      Map organizationProperties) throws Exception {

    /**
     * Only lock on the target values. We don't want lock contention if another
     * node is trying to set the property do a different value
     */
    Lock groupLock = getUniqueUpdateLock(lockManager, MANAGEMENT_APPLICATION_ID, organizationName, "groups", "path");

    Lock userLock = getUniqueUpdateLock(lockManager, MANAGEMENT_APPLICATION_ID, username, "users", "username");

    Lock emailLock = getUniqueUpdateLock(lockManager, MANAGEMENT_APPLICATION_ID, email, "users", "email");

    UserInfo user = null;
    OrganizationInfo organization = null;

    try {

      groupLock.lock();
      userLock.lock();
      emailLock.lock();
      EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
      if (!em.isPropertyValueUniqueForEntity("group", "path", organizationName)) {
        throw new DuplicateUniquePropertyExistsException("group", "path", organizationName);
      }
      if (!validateAdminInfo(username, name, email, password)) {
        return null;
      }
      if (areActivationChecksDisabled()) {
        user = createAdminUserInternal(username, name, email, password, true, false, userProperties);
      } else {
        user = createAdminUserInternal(username, name, email, password, activated, disabled, userProperties);
      }

      organization = createOrganizationInternal(organizationName, user, true, organizationProperties);

    } finally {
      emailLock.unlock();
      userLock.unlock();
      groupLock.unlock();
    }

    return new OrganizationOwnerInfo(user, organization);

  }

  private OrganizationInfo createOrganizationInternal(String organizationName,
                                                      UserInfo user,
                                                      boolean activated) throws Exception {
    return createOrganizationInternal(organizationName, user, activated, null);
  }

  private OrganizationInfo createOrganizationInternal(String organizationName,
                                                      UserInfo user,
                                                      boolean activated,
                                                      Map properties) throws Exception {
    if ((organizationName == null) || (user == null)) {
      return null;
    }
    logger.info("createOrganizationInternal: {}", organizationName);
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

    Group organizationEntity = new Group();
    organizationEntity.setPath(organizationName);
    organizationEntity = em.create(organizationEntity);

    em.addToCollection(organizationEntity, "users", new SimpleEntityRef(User.ENTITY_TYPE, user.getUuid()));

    writeUserToken(MANAGEMENT_APPLICATION_ID, organizationEntity, encryptionService.plainTextCredentials(
        generateOAuthSecretKey(AuthPrincipalType.ORGANIZATION), user.getUuid(), MANAGEMENT_APPLICATION_ID));

    OrganizationInfo organization = new OrganizationInfo(organizationEntity.getUuid(), organizationName, properties);
    updateOrganization(organization);

    logger.info("createOrganizationInternal: {}", organizationName);
    postOrganizationActivity(organization.getUuid(), user, "create", organizationEntity, "Organization",
        organization.getName(), "" + user.getName() + " (" + user.getEmail()
            + ") created a new organization account named " + organizationName, null);

    startOrganizationActivationFlow(organization);

    return organization;
  }

  @Override
  public OrganizationInfo createOrganization(String organizationName, UserInfo user, boolean activated)
      throws Exception {

    if ((organizationName == null) || (user == null)) {
      return null;
    }
    Lock groupLock = getUniqueUpdateLock(lockManager, MANAGEMENT_APPLICATION_ID, organizationName, "groups", "path");
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    if (!em.isPropertyValueUniqueForEntity("group", "path", organizationName)) {
      throw new DuplicateUniquePropertyExistsException("group", "path", organizationName);
    }
    try {
      groupLock.lock();
      return createOrganizationInternal(organizationName, user, activated);
    } finally {
      groupLock.unlock();
    }

  }

  /** currently only affects properties */
  public void updateOrganization(OrganizationInfo organizationInfo) throws Exception {
    Map properties = organizationInfo.getProperties();
    if (properties != null) {
      EntityRef organizationEntity = new SimpleEntityRef(organizationInfo.getUuid());
      EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
      for (Map.Entry entry : properties.entrySet()) {
        if ("".equals(entry.getValue())) {
          properties.remove(entry.getKey());
          em.removeFromDictionary(organizationEntity, ORGANIZATION_PROPERTIES_DICTIONARY, entry.getKey());
        } else {
          em.addToDictionary(organizationEntity, ORGANIZATION_PROPERTIES_DICTIONARY, entry.getKey(), entry.getValue());
        }
      }
    }
  }

  @Override
  public OrganizationInfo importOrganization(UUID organizationId, OrganizationInfo organizationInfo,
      Map properties) throws Exception {

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    if (!em.isPropertyValueUniqueForEntity("group", "path", organizationInfo.getName())) {
      throw new DuplicateUniquePropertyExistsException("group", "path", organizationInfo.getName());
    }
    if (properties == null) {
      properties = new HashMap();
    }

    String organizationName = null;
    if (organizationInfo != null) {
      organizationName = organizationInfo.getName();
    }
    if (organizationName == null) {
      organizationName = (String) properties.get(PROPERTY_PATH);
    }
    if (organizationName == null) {
      organizationName = (String) properties.get(PROPERTY_NAME);
    }
    if (organizationName == null) {
      return null;
    }

    if (organizationId == null) {
      if (organizationInfo != null) {
        organizationId = organizationInfo.getUuid();
      }
    }
    if (organizationId == null) {
      organizationId = uuid(properties.get(PROPERTY_UUID));
    }
    if (organizationId == null) {
      return null;
    }

    properties.put(PROPERTY_PATH, organizationName);
    properties.put(PROPERTY_SECRET, generateOAuthSecretKey(AuthPrincipalType.ORGANIZATION));
    Entity organization = em.create(organizationId, Group.ENTITY_TYPE, properties);
    // em.addToCollection(organization, "users", new SimpleEntityRef(
    // User.ENTITY_TYPE, userId));
    return new OrganizationInfo(organization.getUuid(), organizationName);
  }

  @Override
  public UUID importApplication(UUID organizationId, Application application) throws Exception {
    // TODO organizationName
    OrganizationInfo organization = getOrganizationByUuid(organizationId);
    UUID applicationId = emf.importApplication(organization.getName(), application.getUuid(), application.getName(),
        application.getProperties());

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    properties.setProperty("name", buildAppName(application.getName(), organization));
    Entity app = em.create(applicationId, APPLICATION_INFO, application.getProperties());

    writeUserToken(MANAGEMENT_APPLICATION_ID, app, encryptionService.plainTextCredentials(
        generateOAuthSecretKey(AuthPrincipalType.APPLICATION), null, applicationId));

    addApplicationToOrganization(organizationId, applicationId);
    return applicationId;
  }

  /**
   * Test if the applicationName contains a '/' character, prepend with orgName
   * if it does not, assume it is complete (and that organization is needed) if
   * so.
   * 
   * @param applicationName
   * @param organization
   * @return
   */
  private String buildAppName(String applicationName, OrganizationInfo organization) {
    return applicationName.contains("/") ? applicationName : organization.getName() + "/" + applicationName;
  }

  @Override
  public List getOrganizations(UUID startResult, int count) throws Exception {
    // still need the bimap to search for existing
    BiMap organizations = HashBiMap.create();
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Results results = em.getCollection(em.getApplicationRef(), "groups", startResult, count, Level.ALL_PROPERTIES,
        false);
    List orgs = new ArrayList(results.size());
    OrganizationInfo orgInfo;
    for (Entity entity : results.getEntities()) {
      // TODO T.N. temporary hack to deal with duplicate orgs. Revert this
      // commit after migration
      String path = (String) entity.getProperty("path");

      if (organizations.containsValue(path)) {
        path += "DUPLICATE";
      }
      orgInfo = new OrganizationInfo(entity.getUuid(), path);
      orgs.add(orgInfo);
      organizations.put(entity.getUuid(), path);
    }
    return orgs;
  }

  @Override
  public BiMap getOrganizations() throws Exception {
    List orgs = getOrganizations(null, 10000);
    return buildOrgBiMap(orgs);
  }

  private BiMap buildOrgBiMap(List orgs) {
    BiMap organizations = HashBiMap.create();
    for (OrganizationInfo orgInfo : orgs) {
      organizations.put(orgInfo.getUuid(), orgInfo.getName());
    }
    return organizations;
  }

  @Override
  public OrganizationInfo getOrganizationInfoFromAccessToken(String token) throws Exception {
    Entity entity = getEntityFromAccessToken(token, null, ORGANIZATION);
    if (entity == null) {
      return null;
    }
    return new OrganizationInfo(entity.getProperties());
  }

  @Override
  public OrganizationInfo getOrganizationByName(String organizationName) throws Exception {

    if (organizationName == null) {
      return null;
    }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    EntityRef ref = em.getAlias("group", organizationName);
    if (ref == null) {
      return null;
    }
    return getOrganizationByUuid(ref.getUuid());
  }

  @Override
  public OrganizationInfo getOrganizationByUuid(UUID id) throws Exception {

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Entity entity = em.get(new SimpleEntityRef(Group.ENTITY_TYPE, id));
    if (entity == null) {
      return null;
    }
    Map properties = em.getDictionaryAsMap(entity, ORGANIZATION_PROPERTIES_DICTIONARY);
    OrganizationInfo orgInfo = new OrganizationInfo(entity.getProperties());
    orgInfo.setProperties(properties);
    return orgInfo;
  }

  @Override
  public OrganizationInfo getOrganizationByIdentifier(Identifier id) throws Exception {
    if (id.isUUID()) {
      return getOrganizationByUuid(id.getUUID());
    }
    if (id.isName()) {
      return getOrganizationByName(id.getName());
    }
    return null;
  }

  public void postUserActivity(UserInfo user, String verb, EntityRef object, String objectType, String objectName,
      String title, String content) throws Exception {
    ServiceManager sm = smf.getServiceManager(MANAGEMENT_APPLICATION_ID);

    Map properties = new HashMap();
    properties.put(PROPERTY_VERB, verb);
    properties.put(PROPERTY_CATEGORY, "admin");
    if (content != null) {
      properties.put(PROPERTY_CONTENT, content);
    }
    if (title != null) {
      properties.put(PROPERTY_TITLE, title);
    }
    properties.put(PROPERTY_ACTOR, user.getUuid());
    properties.put(PROPERTY_ACTOR_NAME, user.getName());
    properties.put(PROPERTY_OBJECT, object.getUuid());
    properties.put(PROPERTY_OBJECT_ENTITY_TYPE, object.getType());
    properties.put(PROPERTY_OBJECT_TYPE, objectType);
    properties.put(PROPERTY_OBJECT_NAME, objectName);

    sm.newRequest(ServiceAction.POST, parameters("users", user.getUuid(), "activities"), payload(properties)).execute()
        .getEntity();

  }

  @Override
  public ServiceResults getAdminUserActivities(UserInfo user) throws Exception {
    ServiceManager sm = smf.getServiceManager(MANAGEMENT_APPLICATION_ID);
    ServiceRequest request = sm.newRequest(ServiceAction.GET, parameters("users", user.getUuid(), "feed"));
    ServiceResults results = request.execute();
    return results;
  }

  private UserInfo doCreateAdmin(User user, CredentialsInfo userPassword, CredentialsInfo mongoPassword)
      throws Exception {

    writeUserToken(MANAGEMENT_APPLICATION_ID, user, encryptionService.plainTextCredentials(
        generateOAuthSecretKey(AuthPrincipalType.ADMIN_USER), user.getUuid(), MANAGEMENT_APPLICATION_ID));

    writeUserPassword(MANAGEMENT_APPLICATION_ID, user, userPassword);

    writeUserMongoPassword(MANAGEMENT_APPLICATION_ID, user, mongoPassword);

    UserInfo userInfo = new UserInfo(MANAGEMENT_APPLICATION_ID, user.getUuid(), user.getUsername(), user.getName(),
        user.getEmail(), user.getActivated(), user.getDisabled(), user.getDynamicProperties());

    // special case for sysadmin only
    if (!user.getEmail().equals(properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_EMAIL))) {
      this.startAdminUserActivationFlow(userInfo);
    }

    return userInfo;
  }

  @Override
  public UserInfo createAdminFromPrexistingPassword(User user, CredentialsInfo ci) throws Exception {

    return doCreateAdmin(user, ci,
    // we can't actually set the mongo password. We never have the plain text in
    // this path
        encryptionService.plainTextCredentials(mongoPassword(user.getUsername(), ""), user.getUuid(),
            MANAGEMENT_APPLICATION_ID));

  }

  @Override
  public UserInfo createAdminFrom(User user, String password) throws Exception {
    return doCreateAdmin(user, encryptionService.defaultEncryptedCredentials(password, user.getUuid(),
        MANAGEMENT_APPLICATION_ID), encryptionService.plainTextCredentials(mongoPassword(user.getUsername(), password),
        user.getUuid(), MANAGEMENT_APPLICATION_ID));
  }

  @Override
  public UserInfo createAdminUser(String username, String name, String email, String password, boolean activated,
      boolean disabled) throws Exception {
    return createAdminUser(username, name, email, password, activated, disabled, null);
  }

  @Override
  public UserInfo createAdminUser(String username, String name, String email, String password, boolean activated,
      boolean disabled, Map userProperties) throws Exception {
    if (!validateAdminInfo(username, name, email, password)) {
      return null;
    }
    return createAdminUserInternal(username, name, email, password, activated, disabled, userProperties);
  }

  private boolean validateAdminInfo(String username, String name, String email, String password) throws Exception {
    if (email == null) {
      return false;
    }
    if (username == null) {
      username = email;
    }
    if (name == null) {
      name = email;
    }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

    if (!em.isPropertyValueUniqueForEntity("user", "username", username)) {
      throw new DuplicateUniquePropertyExistsException("user", "username", username);
    }

    if (!em.isPropertyValueUniqueForEntity("user", "email", email)) {
      throw new DuplicateUniquePropertyExistsException("user", "email", email);
    }
    return true;
  }

  private UserInfo createAdminUserInternal(String username, String name, String email, String password,
      boolean activated, boolean disabled, Map userProperties) throws Exception {
    logger.info("createAdminUserInternal: {}", username);

    if (isBlank(password)) {
      password = encodeBase64URLSafeString(bytes(UUID.randomUUID()));
    }
    if (username == null) {
      username = email;
    }
    if (name == null) {
      name = email;
    }
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    User user = new User();
    user.setUsername(username);
    user.setName(name);
    user.setEmail(email);
    user.setActivated(activated);
    user.setConfirmed(!newAdminUsersRequireConfirmation()); // only
    user.setDisabled(disabled);
    if (userProperties != null) {
      // double check no 'password' property just to be safe
      userProperties.remove("password");
      user.setProperties(userProperties);
    }
    user = em.create(user);

    return createAdminFrom(user, password);
  }

  public UserInfo getUserInfo(UUID applicationId, Entity entity) {

    if (entity == null) {
      return null;
    }
    return new UserInfo(applicationId, entity.getUuid(), (String) entity.getProperty("username"), entity.getName(),
        (String) entity.getProperty("email"), ConversionUtils.getBoolean(entity.getProperty("activated")),
        ConversionUtils.getBoolean(entity.getProperty("disabled")), entity.getDynamicProperties());
  }

  public UserInfo getUserInfo(UUID applicationId, Map properties) {

    if (properties == null) {
      return null;
    }
    return new UserInfo(applicationId, properties);
  }

  @Override
  public List getAdminUsersForOrganization(UUID organizationId) throws Exception {

    if (organizationId == null) {
      return null;
    }

    List users = new ArrayList();

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Results results = em.getCollection(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "users", null, 10000,
        Level.ALL_PROPERTIES, false);
    for (Entity entity : results.getEntities()) {
      users.add(getUserInfo(MANAGEMENT_APPLICATION_ID, entity));
    }

    return users;
  }

  @Override
  public UserInfo updateAdminUser(UserInfo user, String username, String name, String email, Map json)
      throws Exception {

    /**
     * Only lock on the target values. We don't want lock contention if another
     * node is trying to set the property do a different value
     */
    Lock usernameLock = getUniqueUpdateLock(lockManager, MANAGEMENT_APPLICATION_ID, username, "users", "username");

    Lock emailLock = getUniqueUpdateLock(lockManager, MANAGEMENT_APPLICATION_ID, email, "users", "email");

    try {

      usernameLock.lock();
      emailLock.lock();

      EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

      SimpleEntityRef entityRef = new SimpleEntityRef(User.ENTITY_TYPE, user.getUuid());
      if (!isBlank(username)) {
        em.setProperty(entityRef, "username", username);
      }
      if (!isBlank(name)) {
        em.setProperty(entityRef, "name", name);
      }
      if (!isBlank(email)) {
        em.setProperty(entityRef, "email", email);
      }
      if (json != null) {
        json.remove("password");
        json.remove("oldpassword");
        json.remove("newpassword");
        Map userProperties = user.getProperties();
        userProperties.putAll(json);
        em.updateProperties(entityRef, userProperties);
      }

      user = getAdminUserByUuid(user.getUuid());
    } finally {
      emailLock.unlock();
      usernameLock.unlock();
    }

    return user;
  }

  public User getAdminUserEntityByEmail(String email) throws Exception {

    if (email == null) {
      return null;
    }

    return getUserEntityByIdentifier(MANAGEMENT_APPLICATION_ID, Identifier.fromEmail(email));
  }

  @Override
  public UserInfo getAdminUserByEmail(String email) throws Exception {
    if (email == null) {
      return null;
    }
    return getUserInfo(MANAGEMENT_APPLICATION_ID,
        getUserEntityByIdentifier(MANAGEMENT_APPLICATION_ID, Identifier.fromEmail(email)));
  }

  public User getUserEntityByIdentifier(UUID applicationId, Identifier indentifier) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    return em.get(em.getUserByIdentifier(indentifier), User.class);
  }

  @Override
  public UserInfo getAdminUserByUsername(String username) throws Exception {
    if (username == null) {
      return null;
    }
    return getUserInfo(MANAGEMENT_APPLICATION_ID,
        getUserEntityByIdentifier(MANAGEMENT_APPLICATION_ID, Identifier.fromName(username)));
  }

  @Override
  public User getAdminUserEntityByUuid(UUID id) throws Exception {
    if (id == null) {
      return null;
    }
    return getUserEntityByIdentifier(MANAGEMENT_APPLICATION_ID, Identifier.fromUUID(id));
  }

  @Override
  public UserInfo getAdminUserByUuid(UUID id) throws Exception {
    return getUserInfo(MANAGEMENT_APPLICATION_ID,
        getUserEntityByIdentifier(MANAGEMENT_APPLICATION_ID, Identifier.fromUUID(id)));
  }

  @Override
  public User getAdminUserEntityByIdentifier(Identifier id) throws Exception {
    return getUserEntityByIdentifier(MANAGEMENT_APPLICATION_ID, id);
  }

  @Override
  public UserInfo getAdminUserByIdentifier(Identifier id) throws Exception {
    if (id.isUUID()) {
      return getAdminUserByUuid(id.getUUID());
    }
    if (id.isName()) {
      return getAdminUserByUsername(id.getName());
    }
    if (id.isEmail()) {
      return getAdminUserByEmail(id.getEmail());
    }
    return null;
  }

  public User findUserEntity(UUID applicationId, String identifier) {

    User user = null;
    if (UUIDUtils.isUUID(identifier)) {
      try {
        Entity entity = getUserEntityByIdentifier(applicationId, Identifier.fromUUID(UUID.fromString(identifier)));
        if (entity != null) {
          user = (User) entity.toTypedEntity();
          logger.info("Found user {} as a UUID", identifier);
        }
      } catch (Exception e) {
        logger.warn("Unable to get user " + identifier + " as a UUID, trying username...");
      }
      return user;
    }
    // now we are either an email or a username. Let Indentifier handle the parsing of such.
    Identifier id = Identifier.from(identifier);

    try {
      Entity entity = getUserEntityByIdentifier(applicationId, id);
      if (entity != null) {
        user = (User) entity.toTypedEntity();
        logger.info("Found user {} as an {}", identifier, id.getType());
      }
    } catch (Exception e) {
      logger.warn("Unable to get user {} as a {}", identifier, id.getType());
    }
    if (user != null) {
      return user;
    }

    return null;
  }

  @Override
  public UserInfo findAdminUser(String identifier) {
    return getUserInfo(MANAGEMENT_APPLICATION_ID, findUserEntity(MANAGEMENT_APPLICATION_ID, identifier));
  }

  @Override
  public void setAdminUserPassword(UUID userId, String oldPassword, String newPassword) throws Exception {

    if ((userId == null) || (oldPassword == null) || (newPassword == null)) {
      return;
    }
    User user = emf.getEntityManager(MANAGEMENT_APPLICATION_ID).get(userId, User.class);

    if (!verify(MANAGEMENT_APPLICATION_ID, user.getUuid(), oldPassword)) {
      logger.info("Old password doesn't match");
      throw new IncorrectPasswordException("Old password does not match");
    }

    setAdminUserPassword(userId, newPassword);
  }

  private static final String CREDENTIALS_HISTORY = "credentialsHistory";

  @Override
  public void setAdminUserPassword(UUID userId, String newPassword) throws Exception {

    if ((userId == null) || (newPassword == null)) { return; }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    User user = em.get(userId, User.class);

    CredentialsInfo newCredentials =
        encryptionService.defaultEncryptedCredentials(newPassword, user.getUuid(), MANAGEMENT_APPLICATION_ID);

    int passwordHistorySize = calculatePasswordHistorySizeForUser(user.getUuid());
    Map credsMap = cast(em.getDictionaryAsMap(user, CREDENTIALS_HISTORY));

    CredentialsInfo currentCredentials = null;
    if (passwordHistorySize > 0) {
      ArrayList oldCreds = new ArrayList(credsMap.values());
      Collections.sort(oldCreds);

      currentCredentials = readUserPasswordCredentials(MANAGEMENT_APPLICATION_ID, user.getUuid());

      // check credential history
      if (encryptionService.verify(newPassword, currentCredentials, userId, MANAGEMENT_APPLICATION_ID)) {
        throw new RecentlyUsedPasswordException();
      }
      for (int i = 0; i < oldCreds.size() && i < passwordHistorySize; i++) {
        CredentialsInfo ci = oldCreds.get(i);
        if (encryptionService.verify(newPassword, ci, userId, MANAGEMENT_APPLICATION_ID)) {
          throw new RecentlyUsedPasswordException();
        }
      }
    }

    // remove excess history
    if (credsMap.size() > passwordHistorySize) {
      ArrayList oldUUIDs = new ArrayList(credsMap.size());
      for (String uuid : credsMap.keySet()) { oldUUIDs.add(UUID.fromString(uuid)); }
      UUIDUtils.sort(oldUUIDs);
      for (int i = 0; i < oldUUIDs.size() - passwordHistorySize; i++) {
        em.removeFromDictionary(user, CREDENTIALS_HISTORY, oldUUIDs.get(i).toString());
      }
    }

    if (passwordHistorySize > 0) {
      UUID uuid = UUIDUtils.newTimeUUID();
      em.addToDictionary(user, CREDENTIALS_HISTORY, uuid.toString(), currentCredentials);
    }

    writeUserPassword(MANAGEMENT_APPLICATION_ID, user, newCredentials);
    writeUserMongoPassword(
        MANAGEMENT_APPLICATION_ID, user,
        encryptionService.plainTextCredentials(
            mongoPassword((String) user.getProperty("username"), newPassword),
            user.getUuid(), MANAGEMENT_APPLICATION_ID));

  }

  public int calculatePasswordHistorySizeForUser(UUID userId) throws Exception {

    int size = 0;
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

    Results orgResults = em.getCollection(new SimpleEntityRef(User.ENTITY_TYPE, userId),
        "groups", null, 10000, Level.REFS, false);

    for (EntityRef orgRef: orgResults.getRefs()) {
      Map properties = em.getDictionaryAsMap(orgRef, ORGANIZATION_PROPERTIES_DICTIONARY);
      if (properties != null) {
        size = Math.max(new OrganizationInfo(null, null, properties).getPasswordHistorySize(), size);
      }
    }

    return size;
  }

  @Override
  public boolean verifyAdminUserPassword(UUID userId, String password) throws Exception {
    if ((userId == null) || (password == null)) {
      return false;
    }
    User user = emf.getEntityManager(MANAGEMENT_APPLICATION_ID).get(userId, User.class);

    return verify(MANAGEMENT_APPLICATION_ID, user.getUuid(), password);
  }

  @Override
  public UserInfo verifyAdminUserPasswordCredentials(String name, String password) throws Exception {
    UserInfo userInfo = null;

    User user = findUserEntity(MANAGEMENT_APPLICATION_ID, name);
    if (user == null) {
      return null;
    }

    if (verify(MANAGEMENT_APPLICATION_ID, user.getUuid(), password)) {
      userInfo = getUserInfo(MANAGEMENT_APPLICATION_ID, user);
      if (!userInfo.isActivated()) {
        throw new UnactivatedAdminUserException();
      }
      if (userInfo.isDisabled()) {
        throw new DisabledAdminUserException();
      }
      return userInfo;
    }
    logger.info("password compare fail for {}", name);
    return null;

  }

  @Override
  public UserInfo verifyMongoCredentials(String name, String nonce, String key) throws Exception {

    Entity user = findUserEntity(MANAGEMENT_APPLICATION_ID, name);

    if (user == null) {
      return null;
    }

    String mongo_pwd = readUserMongoPassword(MANAGEMENT_APPLICATION_ID, user.getUuid()).getSecret();

    if (mongo_pwd == null) {
      throw new IncorrectPasswordException("Your mongo password has not be set");
    }

    String expected_key = DigestUtils.md5Hex(nonce + user.getProperty("username") + mongo_pwd);

    if (!expected_key.equalsIgnoreCase(key)) {
      throw new IncorrectPasswordException();
    }

    UserInfo userInfo = new UserInfo(MANAGEMENT_APPLICATION_ID, user.getProperties());

    if (!userInfo.isActivated()) {
      throw new UnactivatedAdminUserException();
    }
    if (userInfo.isDisabled()) {
      throw new DisabledAdminUserException();
    }

    return userInfo;
  }

  // TokenType tokenType, String type, AuthPrincipalInfo principal,
  // Map state
  public String getTokenForPrincipal(TokenCategory token_category, String token_type, UUID applicationId,
      AuthPrincipalType principal_type, UUID id, long duration) throws Exception {

    if (anyNull(token_category, applicationId, principal_type, id)) {
      return null;
    }

    return tokens.createToken(token_category, token_type, new AuthPrincipalInfo(principal_type, id, applicationId),
        null, duration);

  }

  public void revokeTokensForPrincipal(AuthPrincipalType principalType, UUID applicationId, UUID id) throws Exception {

    if (anyNull(applicationId, principalType, id)) {
      throw new IllegalArgumentException("applicationId, principal_type and id are required");
    }

    AuthPrincipalInfo principal = new AuthPrincipalInfo(principalType, id, applicationId);

    tokens.removeTokens(principal);
  }

  public AuthPrincipalInfo getPrincipalFromAccessToken(String token, String expected_token_type,
      AuthPrincipalType expected_principal_type) throws Exception {

    TokenInfo tokenInfo = tokens.getTokenInfo(token);

    if (tokenInfo == null) {
      return null;
    }

    if ((expected_token_type != null) && !expected_token_type.equals(tokenInfo.getType())) {
      return null;
    }

    AuthPrincipalInfo principal = tokenInfo.getPrincipal();
    if (principal == null) {
      return null;
    }

    if ((expected_principal_type != null) && !expected_principal_type.equals(principal.getType())) {
      return null;
    }

    return principal;
  }

  public Entity getEntityFromAccessToken(String token, String expected_token_type,
      AuthPrincipalType expected_principal_type) throws Exception {

    AuthPrincipalInfo principal = getPrincipalFromAccessToken(token, expected_token_type, expected_principal_type);
    if (principal == null) {
      return null;
    }

    return getEntityFromPrincipal(principal);
  }

  public Entity getEntityFromPrincipal(AuthPrincipalInfo principal) throws Exception {

    EntityManager em = emf.getEntityManager(principal.getApplicationId() != null ? principal.getApplicationId()
        : MANAGEMENT_APPLICATION_ID);
    Entity entity = em.get(principal.getUuid());
    return entity;
  }

  @Override
  public String getAccessTokenForAdminUser(UUID userId, long duration) throws Exception {

    return getTokenForPrincipal(ACCESS, null, MANAGEMENT_APPLICATION_ID, ADMIN_USER, userId, duration);
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * org.usergrid.management.ManagementService#revokeAccessTokensForAdminUser
   * (java.util.UUID)
   */
  @Override
  public void revokeAccessTokensForAdminUser(UUID userId) throws Exception {
    revokeTokensForPrincipal(ADMIN_USER, MANAGEMENT_APPLICATION_ID, userId);
  }

  @Override
  public void revokeAccessTokenForAdminUser(UUID userId, String token) throws Exception {
    if (anyNull(userId, token)) {
      throw new IllegalArgumentException("token is required");
    }

    Entity user = getAdminUserEntityFromAccessToken(token);
    if (!user.getUuid().equals(userId)) {
      throw new TokenException("Could not match token : " + token);
    }

    tokens.revokeToken(token);
  }

  @Override
  public Entity getAdminUserEntityFromAccessToken(String token) throws Exception {

    Entity user = getEntityFromAccessToken(token, null, ADMIN_USER);
    return user;
  }

  @Override
  public UserInfo getAdminUserInfoFromAccessToken(String token) throws Exception {
    Entity user = getAdminUserEntityFromAccessToken(token);
    return new UserInfo(MANAGEMENT_APPLICATION_ID, user.getProperties());
  }

  @Override
  public BiMap getOrganizationsForAdminUser(UUID userId) throws Exception {

    if (userId == null) {
      return null;
    }

    BiMap organizations = HashBiMap.create();
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Results results = em.getCollection(new SimpleEntityRef(User.ENTITY_TYPE, userId), "groups", null, 10000,
        Level.ALL_PROPERTIES, false);

    String path = null;

    for (Entity entity : results.getEntities()) {

      path = (String) entity.getProperty("path");

      if (path != null) {
        path = path.toLowerCase();
      }

      organizations.put(entity.getUuid(), path);
    }

    return organizations;
  }

  @Override
  public Map getAdminUserOrganizationData(UUID userId) throws Exception {
    UserInfo user = getAdminUserByUuid(userId);
    return getAdminUserOrganizationData(user);
  }

  @Override
  public Long getLastAdminPasswordChange(UUID userId) throws Exception {
    CredentialsInfo ci = readUserPasswordCredentials(MANAGEMENT_APPLICATION_ID, userId);
    return ci.getCreated();
  }

  @Override
  public Map getAdminUserOrganizationData(UserInfo user) throws Exception {

    Map json = new HashMap();

    json.putAll(JsonUtils.toJsonMap(user));

    Map> jsonOrganizations = new HashMap>();
    json.put("organizations", jsonOrganizations);

    Map organizations = null;

    boolean superuser_enabled = parseBoolean(properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_ALLOWED));
    String superuser_username = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_NAME);
    if (superuser_enabled && (superuser_username != null) && superuser_username.equals(user.getUsername())) {
      organizations = buildOrgBiMap(getOrganizations(null, 10));
    } else {
      organizations = getOrganizationsForAdminUser(user.getUuid());
    }

    for (Entry organization : organizations.entrySet()) {
      Map jsonOrganization = new HashMap();

      jsonOrganizations.put(organization.getValue(), jsonOrganization);

      jsonOrganization.put(PROPERTY_NAME, organization.getValue());
      jsonOrganization.put(PROPERTY_UUID, organization.getKey());
      jsonOrganization.put("properties", getOrganizationByUuid(organization.getKey()).getProperties());

      BiMap applications = getApplicationsForOrganization(organization.getKey());
      jsonOrganization.put("applications", applications.inverse());

      List users = getAdminUsersForOrganization(organization.getKey());
      Map jsonUsers = new HashMap();
      for (UserInfo u : users) {
        jsonUsers.put(u.getUsername(), u);
      }
      jsonOrganization.put("users", jsonUsers);
    }

    return json;
  }

  @Override
  public Map getOrganizationData(OrganizationInfo organization) throws Exception {

    Map jsonOrganization = new HashMap();
    jsonOrganization.putAll(JsonUtils.toJsonMap(organization));

    BiMap applications = getApplicationsForOrganization(organization.getUuid());
    jsonOrganization.put("applications", applications.inverse());

    List users = getAdminUsersForOrganization(organization.getUuid());
    Map jsonUsers = new HashMap();
    for (UserInfo u : users) {
      jsonUsers.put(u.getUsername(), u);
    }
    jsonOrganization.put("users", jsonUsers);

    return jsonOrganization;
  }

  @Override
  public void addAdminUserToOrganization(UserInfo user, OrganizationInfo organization, boolean email) throws Exception {

    if ((user == null) || (organization == null)) {
      return;
    }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.addToCollection(new SimpleEntityRef(Group.ENTITY_TYPE, organization.getUuid()), "users", new SimpleEntityRef(
        User.ENTITY_TYPE, user.getUuid()));

    if (email) {
      sendAdminUserInvitedEmail(user, organization);
    }
  }

  @Override
  public void removeAdminUserFromOrganization(UUID userId, UUID organizationId) throws Exception {

    if ((userId == null) || (organizationId == null)) {
      return;
    }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

    try {
      if (em.getCollection(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "users", null, 2, Level.IDS, false)
          .size() <= 1) {
        throw new Exception();
      }
    } catch (Exception e) {
      throw new UnableToLeaveOrganizationException("Organizations must have at least one member.");
    }

    em.removeFromCollection(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "users", new SimpleEntityRef(
        User.ENTITY_TYPE, userId));
  }

  @Override
  public ApplicationInfo createApplication(UUID organizationId, String applicationName) throws Exception {

    return createApplication(organizationId, applicationName, null);
  }

  @Override
  public ApplicationInfo createApplication(UUID organizationId, String applicationName, Map properties)
      throws Exception {

    if ((organizationId == null) || (applicationName == null)) {
      return null;
    }

    if (properties == null) {
      properties = new HashMap();
    }

    OrganizationInfo organizationInfo = getOrganizationByUuid(organizationId);

    UUID applicationId = emf.createApplication(organizationInfo.getName(), applicationName, properties);

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    properties.put("name", buildAppName(applicationName, organizationInfo));
    Entity applicationEntity = em.create(applicationId, APPLICATION_INFO, properties);

    writeUserToken(MANAGEMENT_APPLICATION_ID, applicationEntity, encryptionService.plainTextCredentials(
        generateOAuthSecretKey(AuthPrincipalType.APPLICATION), null, MANAGEMENT_APPLICATION_ID));
    addApplicationToOrganization(organizationId, applicationId);

    UserInfo user = null;
    // if we call this method before the full stack is initialized
    // we'll get an exception
    try {
      user = SubjectUtils.getUser();
    } catch (UnavailableSecurityManagerException e) {
    }
    if ((user != null) && user.isAdminUser()) {
      postOrganizationActivity(organizationId, user, "create", applicationEntity, "Application", applicationName,
          "" + user.getName() + " (" + user.getEmail()
              + ") created a new application named " + applicationName, null);
    }
    return new ApplicationInfo(applicationId, applicationEntity.getName());
  }

  @Override
  public OrganizationInfo getOrganizationForApplication(UUID applicationId) throws Exception {

    if (applicationId == null) {
      return null;
    }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Results r = em.getConnectingEntities(applicationId, "owns", "group", Level.ALL_PROPERTIES);
    Entity entity = r.getEntity();
    if (entity != null) {
      return new OrganizationInfo(entity.getUuid(), (String) entity.getProperty("path"));
    }

    return null;
  }

  @Override
  public BiMap getApplicationsForOrganization(UUID organizationId) throws Exception {

    if (organizationId == null) {
      return null;
    }
    BiMap applications = HashBiMap.create();
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Results results = em.getConnectedEntities(organizationId, "owns", APPLICATION_INFO, Level.ALL_PROPERTIES);
    if (!results.isEmpty()) {

      String entityName = null;

      for (Entity entity : results.getEntities()) {
        entityName = entity.getName();

        if (entityName != null) {
          entityName = entityName.toLowerCase();
        }

        applications.put(entity.getUuid(), entityName);

      }
    }

    return applications;
  }

  @Override
  public BiMap getApplicationsForOrganizations(Set organizationIds) throws Exception {
    if (organizationIds == null) {
      return null;
    }
    BiMap applications = HashBiMap.create();
    for (UUID organizationId : organizationIds) {
      BiMap organizationApplications = getApplicationsForOrganization(organizationId);
      applications.putAll(organizationApplications);
    }
    return applications;
  }

  @Override
  public UUID addApplicationToOrganization(UUID organizationId, UUID applicationId) throws Exception {

    if ((organizationId == null) || (applicationId == null)) {
      return null;
    }

    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.createConnection(new SimpleEntityRef("group", organizationId), "owns", new SimpleEntityRef(APPLICATION_INFO,
        applicationId));

    return applicationId;
  }

  @Override
  public void deleteOrganizationApplication(UUID organizationId, UUID applicationId) throws Exception {
    // TODO Auto-generated method stub

  }

  @Override
  public void removeOrganizationApplication(UUID organizationId, UUID applicationId) throws Exception {
    // TODO Auto-generated method stub

  }

  @Override
  public ApplicationInfo getApplicationInfo(String applicationName) throws Exception {
    if (applicationName == null) {
      return null;
    }
    UUID applicationId = emf.lookupApplication(applicationName);
    if (applicationId == null) {
      return null;
    }
    return new ApplicationInfo(applicationId, applicationName.toLowerCase());
  }

  @Override
  public ApplicationInfo getApplicationInfo(UUID applicationId) throws Exception {
    if (applicationId == null) {
      return null;
    }
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    Entity entity = em.get(applicationId);

    if (entity != null) {
      return new ApplicationInfo(applicationId, entity.getName());
    }
    return null;
  }

  @Override
  public ApplicationInfo getApplicationInfo(Identifier id) throws Exception {
    if (id == null) {
      return null;
    }
    if (id.isUUID()) {
      return getApplicationInfo(id.getUUID());
    }
    if (id.isName()) {
      return getApplicationInfo(id.getName());
    }
    return null;
  }

  @Override
  public ApplicationInfo getApplicationInfoFromAccessToken(String token) throws Exception {
    Entity entity = getEntityFromAccessToken(token, null, APPLICATION);
    if (entity == null) {
      throw new TokenException("Could not find an entity for that access token: " + token);
    }
    return new ApplicationInfo(entity.getProperties());
  }

  @Override
  public ServiceResults getApplicationMetadata(UUID applicationId) throws Exception {

    if (applicationId == null) {
      return ServiceResults.genericServiceResults();
    }

    EntityManager em = emf.getEntityManager(applicationId);
    Entity entity = em.get(em.getApplicationRef());

    Results r = Results.fromEntity(entity);

    Map collections = em.getApplicationCollectionMetadata();
    if (collections.size() > 0) {
      r.setMetadata(em.getApplicationRef().getUuid(), "collections", collections);
    }
    return genericServiceResults(r);
  }

  public String getSecret(UUID applicationId, AuthPrincipalType type, UUID id) throws Exception {
    if (AuthPrincipalType.ORGANIZATION.equals(type) || AuthPrincipalType.APPLICATION.equals(type)) {
      UUID ownerId = AuthPrincipalType.APPLICATION_USER.equals(type) ? applicationId : MANAGEMENT_APPLICATION_ID;

      return getCredentialsSecret(readUserToken(ownerId, id));

    } else if (AuthPrincipalType.ADMIN_USER.equals(type) || AuthPrincipalType.APPLICATION_USER.equals(type)) {
      return getCredentialsSecret(readUserPasswordCredentials(applicationId, id));
    }
    throw new IllegalArgumentException("Must specify an admin user, organization or application principal");
  }

  @Override
  public String getClientIdForOrganization(UUID organizationId) {
    return ClientCredentialsInfo.getClientIdForTypeAndUuid(AuthPrincipalType.ORGANIZATION, organizationId);
  }

  @Override
  public String getClientSecretForOrganization(UUID organizationId) throws Exception {
    return getSecret(MANAGEMENT_APPLICATION_ID, AuthPrincipalType.ORGANIZATION, organizationId);
  }

  @Override
  public String getClientIdForApplication(UUID applicationId) {
    return ClientCredentialsInfo.getClientIdForTypeAndUuid(AuthPrincipalType.APPLICATION, applicationId);
  }

  @Override
  public String getClientSecretForApplication(UUID applicationId) throws Exception {
    return getSecret(MANAGEMENT_APPLICATION_ID, AuthPrincipalType.APPLICATION, applicationId);
  }

  public String newSecretKey(AuthPrincipalType type, UUID id) throws Exception {
    String secret = generateOAuthSecretKey(type);

    writeUserToken(MANAGEMENT_APPLICATION_ID, new SimpleEntityRef(type.getEntityType(), id),
        encryptionService.plainTextCredentials(secret, id, MANAGEMENT_APPLICATION_ID));

    return secret;
  }

  @Override
  public String newClientSecretForOrganization(UUID organizationId) throws Exception {
    return newSecretKey(AuthPrincipalType.ORGANIZATION, organizationId);
  }

  @Override
  public String newClientSecretForApplication(UUID applicationId) throws Exception {
    return newSecretKey(AuthPrincipalType.APPLICATION, applicationId);
  }

  @Override
  public AccessInfo authorizeClient(String clientId, String clientSecret, long ttl) throws Exception {
    if ((clientId == null) || (clientSecret == null)) {
      return null;
    }
    UUID uuid = getUUIDFromClientId(clientId);
    if (uuid == null) {
      return null;
    }
    AuthPrincipalType type = getTypeFromClientId(clientId);
    if (type == null) {
      return null;
    }
    AccessInfo access_info = null;
    if (clientSecret.equals(getSecret(MANAGEMENT_APPLICATION_ID, type, uuid))) {
      
      String token = getTokenForPrincipal(ACCESS, null, MANAGEMENT_APPLICATION_ID, type, uuid, ttl);
      
      long duration = tokens.getMaxTokenAgeInSeconds(token);
      
      access_info = new AccessInfo().withExpiresIn(duration).withAccessToken(token);
      
      if (type.equals(AuthPrincipalType.APPLICATION)) {
        ApplicationInfo app = getApplicationInfo(uuid);
        access_info = access_info.withProperty("application", app.getId());
      } else if (type.equals(AuthPrincipalType.ORGANIZATION)) {
        OrganizationInfo organization = getOrganizationByUuid(uuid);
        access_info = access_info
            .withProperty("organization", getOrganizationData(organization));
      }
    }
    return access_info;
  }

  @Override
  public PrincipalCredentialsToken getPrincipalCredentialsTokenForClientCredentials(String clientId, String clientSecret)
      throws Exception {
    if ((clientId == null) || (clientSecret == null)) {
      return null;
    }
    UUID uuid = getUUIDFromClientId(clientId);
    if (uuid == null) {
      return null;
    }
    AuthPrincipalType type = getTypeFromClientId(clientId);
    if (type == null) {
      return null;
    }
    PrincipalCredentialsToken token = null;
    if (clientSecret.equals(getSecret(MANAGEMENT_APPLICATION_ID, type, uuid))) {
      if (type.equals(AuthPrincipalType.APPLICATION)) {
        ApplicationInfo app = getApplicationInfo(uuid);
        token = new PrincipalCredentialsToken(new ApplicationPrincipal(app), new ApplicationClientCredentials(clientId,
            clientSecret));

      } else if (type.equals(AuthPrincipalType.ORGANIZATION)) {
        OrganizationInfo organization = getOrganizationByUuid(uuid);
        token = new PrincipalCredentialsToken(new OrganizationPrincipal(organization),
            new OrganizationClientCredentials(clientId, clientSecret));
      }
    }
    return token;
  }

  public AccessInfo authorizeAppUser(String clientType, String clientId, String clientSecret) throws Exception {

    return null;
  }

  @Override
  public String getPasswordResetTokenForAdminUser(UUID userId, long ttl) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_PASSWORD_RESET, MANAGEMENT_APPLICATION_ID, ADMIN_USER, userId, ttl);
  }

  @Override
  public boolean checkPasswordResetTokenForAdminUser(UUID userId, String token) throws Exception {
    AuthPrincipalInfo principal = null;
    try {
      principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_PASSWORD_RESET, ADMIN_USER);
    } catch (Exception e) {
      logger.error("Unable to verify token", e);
    }
    return (principal != null) && userId.equals(principal.getUuid());
  }

  @Override
  public String getActivationTokenForAdminUser(UUID userId, long ttl) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_ACTIVATION, MANAGEMENT_APPLICATION_ID, ADMIN_USER, userId, ttl);
  }

  @Override
  public String getConfirmationTokenForAdminUser(UUID userId, long ttl) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_CONFIRM, MANAGEMENT_APPLICATION_ID, ADMIN_USER, userId, ttl);
  }

  @Override
  public void activateAdminUser(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "activated", true);
  }

  @Override
  public User deactivateUser(UUID applicationId, UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);

    User user = em.get(userId, User.class);

    if (user == null) {
      throw new ManagementException(String.format("User with id %s does not exist in app %s", userId, applicationId));
    }

    user.setActivated(false);
    user.setDeactivated(System.currentTimeMillis());

    em.update(user);

    // revoke all access tokens for the app
    revokeAccessTokensForAppUser(applicationId, userId);

    return user;
  }

  @Override
  public boolean isAdminUserActivated(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    return Boolean.TRUE.equals(em.getProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "activated"));
  }

  @Override
  public void confirmAdminUser(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "confirmed", true);
  }

  @Override
  public void unconfirmAdminUser(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "confirmed", false);
  }

  @Override
  public boolean isAdminUserConfirmed(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    return Boolean.TRUE.equals(em.getProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "confirmed"));
  }

  @Override
  public void enableAdminUser(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "disabled", false);
  }

  @Override
  public void disableAdminUser(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "disabled", true);

    revokeAccessTokensForAdminUser(userId);
  }

  @Override
  public boolean isAdminUserEnabled(UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    return !Boolean.TRUE.equals(em.getProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "disabled"));
  }

  private String emailMsg(Map values, String propertyName) {
    return new StrSubstitutor(values).replace(properties.getProperty(propertyName));
  }

  private String appendEmailFooter(String msg) {
    return msg + "\n" + properties.getProperty(PROPERTIES_EMAIL_FOOTER);
  }

  @Override
  public void startAdminUserPasswordResetFlow(UserInfo user) throws Exception {
    String token = getPasswordResetTokenForAdminUser(user.getUuid(), 0);

    String reset_url = String.format(properties.getProperty(PROPERTIES_ADMIN_RESETPW_URL), user.getUuid().toString())
        + "?token=" + token;

    Map pageContext = hashMap("reset_url", reset_url)
            .map("reset_url_base",properties.getProperty(PROPERTIES_ADMIN_RESETPW_URL))
            .map("user_uuid", user.getUuid().toString())
            .map("raw_token", token);


    sendHtmlMail(properties, user.getDisplayEmailAddress(), properties.getProperty(PROPERTIES_MAILER_EMAIL),
        "Password Reset",
        appendEmailFooter(emailMsg(pageContext, PROPERTIES_EMAIL_ADMIN_PASSWORD_RESET)));

  }

  @Override
  public String getActivationTokenForOrganization(UUID organizationId, long ttl) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_ACTIVATION, MANAGEMENT_APPLICATION_ID, ORGANIZATION, organizationId,
        ttl);
  }

  @Override
  public void startOrganizationActivationFlow(OrganizationInfo organization) throws Exception {
    logger.info("startOrganizationActivationFlow: {}", organization.getName());

    try {
      String token = getActivationTokenForOrganization(organization.getUuid(), 0);
      String activation_url = String.format(properties.getProperty(PROPERTIES_ORGANIZATION_ACTIVATION_URL),
          organization.getUuid().toString()) + "?token=" + token;
      List users = getAdminUsersForOrganization(organization.getUuid());
      String organization_owners = null;
      for (UserInfo user : users) {
        organization_owners = (organization_owners == null) ? user.getHTMLDisplayEmailAddress() : organization_owners
            + ", " + user.getHTMLDisplayEmailAddress();
      }
      if (newOrganizationsNeedSysAdminApproval()) {
        logger.info("sending SysAdminApproval confirmation email: {}", organization.getName());
        sendHtmlMail(
            properties,
            properties.getProperty(PROPERTIES_SYSADMIN_EMAIL),
            properties.getProperty(PROPERTIES_MAILER_EMAIL),
            "Request For Organization Account Activation " + organization.getName(),
            appendEmailFooter(emailMsg(
                hashMap("organization_name", organization.getName()).map("activation_url", activation_url).map(
                    "organization_owners", organization_owners), PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATION)));
        sendOrganizationEmail(
            organization,
            "Organization Account Confirmed",
            emailMsg(hashMap("organization_name", organization.getName()),
                PROPERTIES_EMAIL_ORGANIZATION_CONFIRMED_AWAITING_ACTIVATION));
      } else if (properties.newOrganizationsRequireConfirmation()) {
        logger.info("sending account confirmation email: {}", organization.getName());
        sendOrganizationEmail(
            organization,
            "Organization Account Confirmation",
            emailMsg(hashMap("organization_name", organization.getName()).map("confirmation_url", activation_url),
                PROPERTIES_EMAIL_ORGANIZATION_CONFIRMATION));
        sendSysAdminNewOrganizationActivatedNotificationEmail(organization);
      } else {
        logger.info("activating organization (no confirmation): {}", organization.getName());
        activateOrganization(organization, false);
        sendSysAdminNewOrganizationActivatedNotificationEmail(organization);
      }
    } catch (Exception e) {
      logger.error("Unable to send activation emails to " + organization.getName(), e);
    }

  }

  @Override
  public ActivationState handleActivationTokenForOrganization(UUID organizationId, String token) throws Exception {
    AuthPrincipalInfo principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_ACTIVATION, ORGANIZATION);
    if ((principal != null) && organizationId.equals(principal.getUuid())) {
      OrganizationInfo organization = this.getOrganizationByUuid(organizationId);
      sendOrganizationActivatedEmail(organization);
      sendSysAdminNewOrganizationActivatedNotificationEmail(organization);

      activateOrganization(organization, false);

      return ActivationState.ACTIVATED;
    }
    return ActivationState.UNKNOWN;
  }

  public void sendOrganizationActivatedEmail(OrganizationInfo organization) throws Exception {
    sendOrganizationEmail(organization, "Organization Account Activated: " + organization.getName(),
        emailMsg(hashMap("organization_name", organization.getName()), PROPERTIES_EMAIL_ORGANIZATION_ACTIVATED));

  }

  public void sendSysAdminNewOrganizationActivatedNotificationEmail(OrganizationInfo organization) throws Exception {
    if (properties.notifySysAdminOfNewOrganizations()) {
      List users = getAdminUsersForOrganization(organization.getUuid());
      String organization_owners = null;
      for (UserInfo user : users) {
        organization_owners = (organization_owners == null) ? user.getHTMLDisplayEmailAddress() : organization_owners
            + ", " + user.getHTMLDisplayEmailAddress();
      }
      sendHtmlMail(
          properties,
          properties.getProperty(PROPERTIES_SYSADMIN_EMAIL),
          properties.getProperty(PROPERTIES_MAILER_EMAIL),
          "Organization Account Activated " + organization.getName(),
          appendEmailFooter(emailMsg(
              hashMap("organization_name", organization.getName()).map("organization_owners", organization_owners),
              PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATED)));
    }

  }

  @Override
  public void sendOrganizationEmail(OrganizationInfo organization, String subject, String html) throws Exception {
    List users = getAdminUsersForOrganization(organization.getUuid());
    for (UserInfo user : users) {
      sendHtmlMail(properties, user.getDisplayEmailAddress(), properties.getProperty(PROPERTIES_MAILER_EMAIL), subject,
          appendEmailFooter(html));
    }

  }

  @Override
  public void startAdminUserActivationFlow(UserInfo user) throws Exception {
    if (user.isActivated()) {
      sendAdminUserConfirmationEmail(user);
      sendAdminUserActivatedEmail(user);
      sendSysAdminNewAdminActivatedNotificationEmail(user);
    } else {
      if (newAdminUsersRequireConfirmation()) {
        sendAdminUserConfirmationEmail(user);
      } else if (newAdminUsersNeedSysAdminApproval()) {
        sendSysAdminRequestAdminActivationEmail(user);
      } else {
        // sdg: There seems to be a hole in the logic. The user has been
        // created
        // in an inactive state but nobody is being notified.
        activateAdminUser(user.getUuid());
      }
    }
  }

  @Override
  public ActivationState handleConfirmationTokenForAdminUser(UUID userId, String token) throws Exception {
    AuthPrincipalInfo principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_CONFIRM, ADMIN_USER);
    if ((principal != null) && userId.equals(principal.getUuid())) {
      UserInfo user = getAdminUserByUuid(principal.getUuid());
      confirmAdminUser(user.getUuid());
      if (newAdminUsersNeedSysAdminApproval()) {
        sendAdminUserConfirmedAwaitingActivationEmail(user);
        sendSysAdminRequestAdminActivationEmail(user);
        return ActivationState.CONFIRMED_AWAITING_ACTIVATION;
      } else {
        activateAdminUser(principal.getUuid());
        sendAdminUserActivatedEmail(user);
        sendSysAdminNewAdminActivatedNotificationEmail(user);
        return ActivationState.ACTIVATED;
      }
    }
    return ActivationState.UNKNOWN;
  }

  @Override
  public ActivationState handleActivationTokenForAdminUser(UUID userId, String token) throws Exception {
    AuthPrincipalInfo principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_ACTIVATION, ADMIN_USER);
    if ((principal != null) && userId.equals(principal.getUuid())) {
      activateAdminUser(principal.getUuid());
      UserInfo user = getAdminUserByUuid(principal.getUuid());
      sendAdminUserActivatedEmail(user);
      sendSysAdminNewAdminActivatedNotificationEmail(user);
      return ActivationState.ACTIVATED;
    }
    return ActivationState.UNKNOWN;
  }

  public void sendAdminUserConfirmationEmail(UserInfo user) throws Exception {
    String token = getConfirmationTokenForAdminUser(user.getUuid(), 0);
    String confirmation_url = String.format(properties.getProperty(PROPERTIES_ADMIN_CONFIRMATION_URL), user.getUuid()
        .toString())
        + "?token=" + token;
    sendAdminUserEmail(
        user,
        "User Account Confirmation: " + user.getEmail(),
        emailMsg(hashMap("user_email", user.getEmail()).map("confirmation_url", confirmation_url),
            PROPERTIES_EMAIL_ADMIN_CONFIRMATION));

  }

  public void sendSysAdminRequestAdminActivationEmail(UserInfo user) throws Exception {
    String token = getActivationTokenForAdminUser(user.getUuid(), 0);
    String activation_url = String.format(properties.getProperty(PROPERTIES_ADMIN_ACTIVATION_URL), user.getUuid()
        .toString())
        + "?token=" + token;
    sendHtmlMail(
        properties,
        properties.getProperty(PROPERTIES_SYSADMIN_EMAIL),
        properties.getProperty(PROPERTIES_MAILER_EMAIL),
        "Request For Admin User Account Activation " + user.getEmail(),
        appendEmailFooter(emailMsg(hashMap("user_email", user.getEmail()).map("activation_url", activation_url),
            PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATION)));
  }

  public void sendSysAdminNewAdminActivatedNotificationEmail(UserInfo user) throws Exception {
    if (properties.notifySysAdminOfNewAdminUsers()) {
      sendHtmlMail(
          properties,
          properties.getProperty(PROPERTIES_SYSADMIN_EMAIL),
          properties.getProperty(PROPERTIES_MAILER_EMAIL),
          "Admin User Account Activated " + user.getEmail(),
          appendEmailFooter(emailMsg(hashMap("user_email", user.getEmail()), PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATED)));
    }
  }

  public void sendAdminUserConfirmedAwaitingActivationEmail(UserInfo user) throws Exception {
    sendAdminUserEmail(user, "User Account Confirmed",
        properties.getProperty(PROPERTIES_EMAIL_ADMIN_CONFIRMED_AWAITING_ACTIVATION));

  }

  public void sendAdminUserActivatedEmail(UserInfo user) throws Exception {
    if (properties.notifyAdminOfActivation()) {
      sendAdminUserEmail(user, "User Account Activated", properties.getProperty(PROPERTIES_EMAIL_ADMIN_ACTIVATED));
    }
  }

  public void sendAdminUserInvitedEmail(UserInfo user, OrganizationInfo organization) throws Exception {
    sendAdminUserEmail(user, "User Invited To Organization",
        emailMsg(hashMap("organization_name", organization.getName()), PROPERTIES_EMAIL_ADMIN_INVITED));

  }

  @Override
  public void sendAdminUserEmail(UserInfo user, String subject, String html) throws Exception {
    sendHtmlMail(properties, user.getDisplayEmailAddress(), properties.getProperty(PROPERTIES_MAILER_EMAIL), subject,
        appendEmailFooter(html));

  }

  @Override
  public void activateOrganization(OrganizationInfo organization) throws Exception {
    activateOrganization(organization, true);
  }

  private void activateOrganization(OrganizationInfo organization, boolean sendEmail) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(Group.ENTITY_TYPE, organization.getUuid()), "activated", true);
    List users = getAdminUsersForOrganization(organization.getUuid());
    for (UserInfo user : users) {
      if (!user.isActivated()) {
        activateAdminUser(user.getUuid());
      }
    }
    if (sendEmail) {
      startOrganizationActivationFlow(organization);
    }
  }

  @Override
  public void deactivateOrganization(UUID organizationId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "activated", false);
  }

  @Override
  public boolean isOrganizationActivated(UUID organizationId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    return Boolean.TRUE.equals(em.getProperty(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "activated"));
  }

  @Override
  public void enableOrganization(UUID organizationId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "disabled", false);
  }

  @Override
  public void disableOrganization(UUID organizationId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.setProperty(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "disabled", true);
  }

  @Override
  public boolean isOrganizationEnabled(UUID organizationId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    return !Boolean.TRUE.equals(em.getProperty(new SimpleEntityRef(Group.ENTITY_TYPE, organizationId), "disabled"));
  }

  @Override
  public boolean checkPasswordResetTokenForAppUser(UUID applicationId, UUID userId, String token) throws Exception {
    AuthPrincipalInfo principal = null;
    try {
      principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_PASSWORD_RESET, APPLICATION_USER);
    } catch (Exception e) {
      logger.error("Unable to verify token", e);
    }
    return (principal != null) && userId.equals(principal.getUuid());
  }

  @Override
  public String getAccessTokenForAppUser(UUID applicationId, UUID userId, long duration) throws Exception {
    return getTokenForPrincipal(ACCESS, null, applicationId, APPLICATION_USER, userId, duration);
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * org.usergrid.management.ManagementService#revokeAccessTokensForAappUser
   * (java.util.UUID, java.util.UUID)
   */
  @Override
  public void revokeAccessTokensForAppUser(UUID applicationId, UUID userId) throws Exception {
    revokeTokensForPrincipal(APPLICATION_USER, applicationId, userId);
  }

  @Override
  public void revokeAccessTokenForAppUser(String token) throws Exception {
    if (anyNull(token)) {
      throw new IllegalArgumentException("token is required");
    }

    UserInfo userInfo = getAppUserFromAccessToken(token);
    if (userInfo == null) {
      throw new TokenException("Could not match token : " + token);
    }

    tokens.revokeToken(token);
  }

  @Override
  public UserInfo getAppUserFromAccessToken(String token) throws Exception {
    AuthPrincipalInfo auth_principal = getPrincipalFromAccessToken(token, null, APPLICATION_USER);
    if (auth_principal == null) {
      return null;
    }
    UUID appId = auth_principal.getApplicationId();
    if (appId != null) {
      Entity user = getAppUserByIdentifier(appId, Identifier.fromUUID(auth_principal.getUuid()));
      if (user != null) {
        return new UserInfo(appId, user.getProperties());
      }
    }
    return null;
  }

  @Override
  public User getAppUserByIdentifier(UUID applicationId, Identifier identifier) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    return em.get(em.getUserByIdentifier(identifier), User.class);
  }

  @Override
  public void startAppUserPasswordResetFlow(UUID applicationId, User user) throws Exception {
    String token = getPasswordResetTokenForAppUser(applicationId, user.getUuid());
    String reset_url = buildUserAppUrl(applicationId, properties.getProperty(PROPERTIES_USER_RESETPW_URL), user, token);
    Map pageContext = hashMap("reset_url", reset_url)
            .map("reset_url_base",properties.getProperty(PROPERTIES_ADMIN_RESETPW_URL))
            .map("user_uuid", user.getUuid().toString())
            .map("raw_token", token)
            .map("application_id", applicationId.toString());
    /*
     * String reset_url = String.format(
     * properties.getProperty(PROPERTIES_USER_RESETPW_URL), oi.getName(),
     * ai.getName(), user.getUuid().toString()) + "?token=" + token;
     */
    sendHtmlMail(properties, user.getDisplayEmailAddress(), properties.getProperty(PROPERTIES_MAILER_EMAIL),
        "Password Reset",
        appendEmailFooter(emailMsg(pageContext, PROPERTIES_EMAIL_USER_PASSWORD_RESET)));

  }

  @Override
  public boolean newAppUsersNeedAdminApproval(UUID applicationId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    Boolean registration_requires_admin_approval = (Boolean) em.getProperty(new SimpleEntityRef(
        Application.ENTITY_TYPE, applicationId), "registration_requires_admin_approval");
    return registration_requires_admin_approval != null ? registration_requires_admin_approval.booleanValue() : false;
  }

  @Override
  public boolean newAppUsersRequireConfirmation(UUID applicationId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    Boolean registration_requires_email_confirmation = (Boolean) em.getProperty(new SimpleEntityRef(
        Application.ENTITY_TYPE, applicationId), "registration_requires_email_confirmation");
    return registration_requires_email_confirmation != null ? registration_requires_email_confirmation.booleanValue()
        : false;
  }

  public boolean notifyAdminOfNewAppUsers(UUID applicationId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    Boolean notify_admin_of_new_users = (Boolean) em.getProperty(new SimpleEntityRef(Application.ENTITY_TYPE,
        applicationId), "notify_admin_of_new_users");
    return notify_admin_of_new_users != null ? notify_admin_of_new_users.booleanValue() : false;
  }

  @Override
  public void startAppUserActivationFlow(UUID applicationId, User user) throws Exception {
    if (newAppUsersRequireConfirmation(applicationId)) {
      sendAppUserConfirmationEmail(applicationId, user);
    } else if (newAppUsersNeedAdminApproval(applicationId)) {
      sendAdminRequestAppUserActivationEmail(applicationId, user);
    } else {
      sendAppUserActivatedEmail(applicationId, user);
      sendAdminNewAppUserActivatedNotificationEmail(applicationId, user);
    }
  }

  @Override
  public ActivationState handleConfirmationTokenForAppUser(UUID applicationId, UUID userId, String token)
      throws Exception {
    AuthPrincipalInfo principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_CONFIRM, APPLICATION_USER);
    if ((principal != null) && userId.equals(principal.getUuid())) {
      EntityManager em = emf.getEntityManager(applicationId);
      User user = em.get(userId, User.class);
      confirmAppUser(applicationId, user.getUuid());
      if (newAppUsersNeedAdminApproval(applicationId)) {
        sendAppUserConfirmedAwaitingActivationEmail(applicationId, user);
        sendAdminRequestAppUserActivationEmail(applicationId, user);
        return ActivationState.CONFIRMED_AWAITING_ACTIVATION;
      } else {
        activateAppUser(applicationId, principal.getUuid());
        sendAppUserActivatedEmail(applicationId, user);
        sendAdminNewAppUserActivatedNotificationEmail(applicationId, user);
        return ActivationState.ACTIVATED;
      }
    }
    return ActivationState.UNKNOWN;
  }

  @Override
  public ActivationState handleActivationTokenForAppUser(UUID applicationId, UUID userId, String token)
      throws Exception {
    AuthPrincipalInfo principal = getPrincipalFromAccessToken(token, TOKEN_TYPE_ACTIVATION, APPLICATION_USER);
    if ((principal != null) && userId.equals(principal.getUuid())) {
      activateAppUser(applicationId, principal.getUuid());
      EntityManager em = emf.getEntityManager(applicationId);
      User user = em.get(userId, User.class);
      sendAppUserActivatedEmail(applicationId, user);
      sendAdminNewAppUserActivatedNotificationEmail(applicationId, user);
      return ActivationState.ACTIVATED;
    }
    return ActivationState.UNKNOWN;
  }

  public void sendAppUserConfirmationEmail(UUID applicationId, User user) throws Exception {
    String token = getConfirmationTokenForAppUser(applicationId, user.getUuid());
    String confirmation_url = buildUserAppUrl(applicationId, properties.getProperty(PROPERTIES_USER_CONFIRMATION_URL),
        user, token);

    /*
     * String confirmation_url = String.format(
     * properties.getProperty(PROPERTIES_USER_CONFIRMATION_URL),
     * applicationId.toString(), user.getUuid().toString()) + "?token=" + token;
     */
    sendAppUserEmail(user, "User Account Confirmation: " + user.getEmail(),
        emailMsg(hashMap("confirmation_url", confirmation_url), PROPERTIES_EMAIL_USER_CONFIRMATION));

  }

  private String buildUserAppUrl(UUID applicationId, String url, User user, String token) throws Exception {
    ApplicationInfo ai = getApplicationInfo(applicationId);
    OrganizationInfo oi = getOrganizationForApplication(applicationId);
    return String.format(url, oi.getName(), StringUtils.stringOrSubstringAfterFirst(ai.getName(), '/'), user.getUuid()
        .toString())
        + "?token=" + token;
  }

  public void sendAdminRequestAppUserActivationEmail(UUID applicationId, User user) throws Exception {
    String token = getActivationTokenForAppUser(applicationId, user.getUuid());
    String activation_url = buildUserAppUrl(applicationId, properties.getProperty(PROPERTIES_USER_ACTIVATION_URL),
        user, token);
    /*
     * String activation_url = String.format(
     * properties.getProperty(PROPERTIES_USER_ACTIVATION_URL),
     * applicationId.toString(), user.getUuid().toString()) + "?token=" + token;
     */
    OrganizationInfo organization = this.getOrganizationForApplication(applicationId);
    this.sendOrganizationEmail(
        organization,
        "Request For User Account Activation " + user.getEmail(),
        emailMsg(hashMap("organization_name", organization.getName()).map("activation_url", activation_url),
            PROPERTIES_EMAIL_ADMIN_USER_ACTIVATION));
  }

  public void sendAdminNewAppUserActivatedNotificationEmail(UUID applicationId, User user) throws Exception {
    if (notifyAdminOfNewAppUsers(applicationId)) {
      OrganizationInfo organization = this.getOrganizationForApplication(applicationId);
      this.sendOrganizationEmail(organization, "New User Account Activated " + user.getEmail(),
          emailMsg(hashMap("organization_name", organization.getName()), PROPERTIES_EMAIL_ADMIN_USER_ACTIVATION));
    }
  }

  public void sendAppUserConfirmedAwaitingActivationEmail(UUID applicationId, User user) throws Exception {
    sendAppUserEmail(user, "User Account Confirmed",
        properties.getProperty(PROPERTIES_EMAIL_USER_CONFIRMED_AWAITING_ACTIVATION));

  }

  public void sendAppUserActivatedEmail(UUID applicationId, User user) throws Exception {
    sendAppUserEmail(user, "User Account Activated", properties.getProperty(PROPERTIES_EMAIL_USER_ACTIVATED));
  }

  @Override
  public void activateAppUser(UUID applicationId, UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "activated", true);
  }

  public void confirmAppUser(UUID applicationId, UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    em.setProperty(new SimpleEntityRef(User.ENTITY_TYPE, userId), "confirmed", true);
  }

  @Override
  public void setAppUserPassword(UUID applicationId, UUID userId, String newPassword) throws Exception {
    if ((userId == null) || (newPassword == null)) {
      return;
    }

    EntityManager em = emf.getEntityManager(applicationId);
    User user = em.get(userId, User.class);

    writeUserPassword(applicationId, user,
        encryptionService.defaultEncryptedCredentials(newPassword, user.getUuid(), applicationId));

  }

  @Override
  public void setAppUserPassword(UUID applicationId, UUID userId, String oldPassword, String newPassword)
      throws Exception {
    if ((userId == null)) {
      throw new IllegalArgumentException("userId is required");
    }
    if ((oldPassword == null) || (newPassword == null)) {
      throw new IllegalArgumentException("oldpassword and newpassword are both required");
    }
    // TODO load the user, send the hashType down to maybeSaltPassword
    User user = emf.getEntityManager(applicationId).get(userId, User.class);
    if (!verify(applicationId, user.getUuid(), oldPassword)) {
      throw new IncorrectPasswordException("Old password does not match");
    }

    setAppUserPassword(applicationId, userId, newPassword);

  }

  @Override
  public User verifyAppUserPasswordCredentials(UUID applicationId, String name, String password) throws Exception {

    User user = findUserEntity(applicationId, name);
    if (user == null) {
      return null;
    }

    if (verify(applicationId, user.getUuid(), password)) {
      if (!user.activated()) {
        throw new UnactivatedAppUserException();
      }
      if (user.disabled()) {
        throw new DisabledAppUserException();
      }
      return user;
    }

    return null;
  }

  public String getPasswordResetTokenForAppUser(UUID applicationId, UUID userId) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_PASSWORD_RESET, applicationId, APPLICATION_USER, userId, 0);
  }

  public void sendAppUserEmail(User user, String subject, String html) throws Exception {
    sendHtmlMail(properties, user.getDisplayEmailAddress(), properties.getProperty(PROPERTIES_MAILER_EMAIL), subject,
        appendEmailFooter(html));

  }

  public String getActivationTokenForAppUser(UUID applicationId, UUID userId) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_ACTIVATION, applicationId, APPLICATION_USER, userId, 0);
  }

  public String getConfirmationTokenForAppUser(UUID applicationId, UUID userId) throws Exception {
    return getTokenForPrincipal(EMAIL, TOKEN_TYPE_CONFIRM, applicationId, APPLICATION_USER, userId, 0);
  }

  @Override
  public void setAppUserPin(UUID applicationId, UUID userId, String newPin) throws Exception {
    if ((userId == null) || (newPin == null)) {
      return;
    }

    writeUserPin(applicationId, new SimpleEntityRef(User.ENTITY_TYPE, userId),
        encryptionService.plainTextCredentials(newPin, userId, applicationId));
  }

  @Override
  public void sendAppUserPin(UUID applicationId, UUID userId) throws Exception {
    EntityManager em = emf.getEntityManager(applicationId);
    User user = em.get(userId, User.class);
    if (user == null) {
      return;
    }
    if (user.getEmail() == null) {
      return;
    }
    String pin = getCredentialsSecret(readUserPin(applicationId, userId));

    sendHtmlMail(properties, user.getDisplayEmailAddress(), properties.getProperty(PROPERTIES_MAILER_EMAIL),
        "Your app pin", appendEmailFooter(emailMsg(hashMap(USER_PIN, pin), PROPERTIES_EMAIL_USER_PIN_REQUEST)));

  }

  @Override
  public User verifyAppUserPinCredentials(UUID applicationId, String name, String pin) throws Exception {

    User user = findUserEntity(applicationId, name);
    if (user == null) {
      return null;
    }
    if (pin.equals(getCredentialsSecret(readUserPin(applicationId, user.getUuid())))) {
      return user;
    }
    return null;
  }

  @Override
  public void countAdminUserAction(UserInfo user, String action) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
    em.incrementAggregateCounters(user.getUuid(), null, null, "admin_logins", 1);

  }


  /*
   * (non-Javadoc)
   * 
   * @see
   * org.usergrid.management.ManagementService#setOrganizationProps(java.util
   * .UUID, java.util.Map)
   */
  @Override
  public void setOrganizationProps(UUID orgId, Map props) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

    Group org = em.get(orgId, Group.class);

    if (org == null) {
      throw new EntityNotFoundException(String.format("Could not find organization with id {}", orgId));
    }

    org.setProperties(props);

    em.update(org);
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * org.usergrid.management.ManagementService#getOrganizationProps(java.util
   * .UUID)
   */
  @Override
  public Group getOrganizationProps(UUID orgId) throws Exception {
    EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);

    return em.get(orgId, Group.class);

  }

  /**
   * Persist the user's password credentials info
   * 
   * @param appId
   * @param owner
   * @param creds
   * @throws Exception
   */
  protected void writeUserPassword(UUID appId, EntityRef owner, CredentialsInfo creds) throws Exception {
    writeCreds(appId, owner, creds, USER_PASSWORD);
  }

  /**
   * read the user password credential's info
   * 
   * @param appId
   * @param ownerId
   * @return
   * @throws Exception
   */
  protected CredentialsInfo readUserPasswordCredentials(UUID appId, UUID ownerId) throws Exception {
    return readCreds(appId, ownerId, USER_PASSWORD);
  }

  /**
   * Write the user's token
   * 
   * @param appId
   * @param owner
   * @param token
   * @throws Exception
   */
  protected void writeUserToken(UUID appId, EntityRef owner, CredentialsInfo token) throws Exception {
    writeCreds(appId, owner, token, USER_TOKEN);
  }

  /**
   * Read the credentials info for the user's token
   * 
   * @param appId
   * @param ownerId
   * @return
   * @throws Exception
   */
  protected CredentialsInfo readUserToken(UUID appId, UUID ownerId) throws Exception {
    return readCreds(appId, ownerId, USER_TOKEN);
  }

  /**
   * Write the mongo password
   * 
   * @param appId
   * @param owner
   * @param password
   * @throws Exception
   */
  protected void writeUserMongoPassword(UUID appId, EntityRef owner, CredentialsInfo password) throws Exception {
    writeCreds(appId, owner, password, USER_MONGO_PASSWORD);
  }

  /**
   * Read the mongo password
   * 
   * @param appId
   * @param ownerId
   * @return
   * @throws Exception
   */
  protected CredentialsInfo readUserMongoPassword(UUID appId, UUID ownerId) throws Exception {
    return readCreds(appId, ownerId, USER_MONGO_PASSWORD);
  }

  /**
   * Write the user's pin
   * 
   * @param appId
   * @param owner
   * @param pin
   * @throws Exception
   */
  protected void writeUserPin(UUID appId, EntityRef owner, CredentialsInfo pin) throws Exception {
    writeCreds(appId, owner, pin, USER_PIN);
  }

  /**
   * Read the user's pin
   * 
   * @param appId
   * @param ownerId
   * @return
   * @throws Exception
   */
  protected CredentialsInfo readUserPin(UUID appId, UUID ownerId) throws Exception {
    return readCreds(appId, ownerId, USER_PIN);
  }

  private void writeCreds(UUID appId, EntityRef owner, CredentialsInfo creds, String key) throws Exception {
    EntityManager em = emf.getEntityManager(appId);
    em.addToDictionary(owner, DICTIONARY_CREDENTIALS, key, creds);

  }

  private CredentialsInfo readCreds(UUID appId, UUID ownerId, String key) throws Exception {
    EntityManager em = emf.getEntityManager(appId);
    Entity owner = em.get(ownerId);
    return (CredentialsInfo) em.getDictionaryElementValue(owner, DICTIONARY_CREDENTIALS, key);
  }

  private Set readUserPasswordHistory(UUID appId, UUID ownerId) throws Exception {
    EntityManager em = emf.getEntityManager(appId);
    Entity owner = em.get(ownerId);
    return (Set) em.getDictionaryElementValue(owner, DICTIONARY_CREDENTIALS, USER_PASSWORD_HISTORY);
  }

  @Override
  public boolean newAdminUsersNeedSysAdminApproval() {
    return properties.newAdminUsersNeedSysAdminApproval();
  }

  @Override
  public boolean newAdminUsersRequireConfirmation() {
    return properties.newAdminUsersRequireConfirmation();
  }

  @Override
  public boolean newOrganizationsNeedSysAdminApproval() {
    return properties.newOrganizationsNeedSysAdminApproval();
  }

  private boolean areActivationChecksDisabled() {
    return !(newOrganizationsNeedSysAdminApproval() || properties.newOrganizationsRequireConfirmation()
        || newAdminUsersNeedSysAdminApproval() || newAdminUsersRequireConfirmation());
  }

  private void sendHtmlMail(AccountCreationProps props, String to, String from, String subject, String html) {
    mailUtils.sendHtmlMail(props.getMailProperties(), to, from, subject, html);
  }

  public AccountCreationProps getAccountCreationProps() {
    return properties;
  }

  private boolean verify(UUID applicationId, UUID userId, String password) throws Exception {
    CredentialsInfo ci = readUserPasswordCredentials(applicationId, userId);

    if (ci == null) {
      return false;
    }

    return encryptionService.verify(password, ci, userId, applicationId);
  }

  /**
   * @return the saltProvider
   */
  public SaltProvider getSaltProvider() {
    return saltProvider;
  }

  /**
   * @param saltProvider
   *          the saltProvider to set
   */
  public void setSaltProvider(SaltProvider saltProvider) {
    this.saltProvider = saltProvider;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy