org.apache.usergrid.management.cassandra.ManagementServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.usergrid.management.cassandra.ManagementServiceImpl.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.usergrid.management.cassandra;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.shiro.UnavailableSecurityManagerException;

import org.apache.usergrid.locking.Lock;
import org.apache.usergrid.locking.LockManager;
import org.apache.usergrid.management.AccountCreationProps;
import org.apache.usergrid.management.ActivationState;
import org.apache.usergrid.management.ApplicationInfo;
import org.apache.usergrid.management.ManagementService;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.OrganizationOwnerInfo;
import org.apache.usergrid.management.UserInfo;
import org.apache.usergrid.management.exceptions.DisabledAdminUserException;
import org.apache.usergrid.management.exceptions.DisabledAppUserException;
import org.apache.usergrid.management.exceptions.IncorrectPasswordException;
import org.apache.usergrid.management.exceptions.ManagementException;
import org.apache.usergrid.management.exceptions.RecentlyUsedPasswordException;
import org.apache.usergrid.management.exceptions.UnableToLeaveOrganizationException;
import org.apache.usergrid.management.exceptions.UnactivatedAdminUserException;
import org.apache.usergrid.management.exceptions.UnactivatedAppUserException;
import org.apache.usergrid.management.exceptions.UnconfirmedAdminUserException;
import org.apache.usergrid.persistence.CredentialsInfo;
import org.apache.usergrid.persistence.Entity;
import org.apache.usergrid.persistence.EntityManager;
import org.apache.usergrid.persistence.EntityManagerFactory;
import org.apache.usergrid.persistence.EntityRef;
import org.apache.usergrid.persistence.Identifier;
import org.apache.usergrid.persistence.PagingResultsIterator;
import org.apache.usergrid.persistence.Results;
import org.apache.usergrid.persistence.Results.Level;
import org.apache.usergrid.persistence.SimpleEntityRef;
import org.apache.usergrid.persistence.entities.Application;
import org.apache.usergrid.persistence.entities.Group;
import org.apache.usergrid.persistence.entities.User;
import org.apache.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException;
import org.apache.usergrid.persistence.exceptions.EntityNotFoundException;
import org.apache.usergrid.security.AuthPrincipalInfo;
import org.apache.usergrid.security.AuthPrincipalType;
import org.apache.usergrid.security.crypto.EncryptionService;
import org.apache.usergrid.security.oauth.AccessInfo;
import org.apache.usergrid.security.oauth.ClientCredentialsInfo;
import org.apache.usergrid.security.salt.SaltProvider;
import org.apache.usergrid.security.shiro.PrincipalCredentialsToken;
import org.apache.usergrid.security.shiro.credentials.ApplicationClientCredentials;
import org.apache.usergrid.security.shiro.credentials.OrganizationClientCredentials;
import org.apache.usergrid.security.shiro.principals.ApplicationPrincipal;
import org.apache.usergrid.security.shiro.principals.OrganizationPrincipal;
import org.apache.usergrid.security.shiro.utils.SubjectUtils;
import org.apache.usergrid.security.tokens.TokenCategory;
import org.apache.usergrid.security.tokens.TokenInfo;
import org.apache.usergrid.security.tokens.TokenService;
import org.apache.usergrid.security.tokens.exceptions.TokenException;
import org.apache.usergrid.services.ServiceAction;
import org.apache.usergrid.services.ServiceManager;
import org.apache.usergrid.services.ServiceManagerFactory;
import org.apache.usergrid.services.ServiceRequest;
import org.apache.usergrid.services.ServiceResults;
import org.apache.usergrid.utils.ConversionUtils;
import org.apache.usergrid.utils.JsonUtils;
import org.apache.usergrid.utils.MailUtils;
import org.apache.usergrid.utils.StringUtils;
import org.apache.usergrid.utils.UUIDUtils;

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

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.apache.usergrid.locking.LockHelper.getUniqueUpdateLock;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_ACTIVATION_URL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_CONFIRMATION_URL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_ADMIN_RESETPW_URL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_ACTIVATED;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_CONFIRMATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_CONFIRMED_AWAITING_ACTIVATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_INVITED;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_PASSWORD_RESET;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ADMIN_USER_ACTIVATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_FOOTER;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_ACTIVATED;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_CONFIRMATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_ORGANIZATION_CONFIRMED_AWAITING_ACTIVATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATED;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ADMIN_ACTIVATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATED;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_SYSADMIN_ORGANIZATION_ACTIVATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_ACTIVATED;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_CONFIRMATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_CONFIRMED_AWAITING_ACTIVATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_PASSWORD_RESET;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_EMAIL_USER_PIN_REQUEST;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_MAILER_EMAIL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_ORGANIZATION_ACTIVATION_URL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_SETUP_TEST_ACCOUNT;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_EMAIL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_SYSADMIN_LOGIN_EMAIL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_EMAIL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_NAME;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_PASSWORD;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ADMIN_USER_USERNAME;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_APP;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_TEST_ACCOUNT_ORGANIZATION;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_USER_ACTIVATION_URL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_USER_CONFIRMATION_URL;
import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_USER_RESETPW_URL;
import static org.apache.usergrid.persistence.CredentialsInfo.getCredentialsSecret;
import static org.apache.usergrid.persistence.Schema.DICTIONARY_CREDENTIALS;
import static org.apache.usergrid.persistence.Schema.PROPERTY_NAME;
import static org.apache.usergrid.persistence.Schema.PROPERTY_PATH;
import static org.apache.usergrid.persistence.Schema.PROPERTY_SECRET;
import static org.apache.usergrid.persistence.Schema.PROPERTY_UUID;
import static org.apache.usergrid.persistence.cassandra.CassandraService.MANAGEMENT_APPLICATION_ID;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_ACTOR;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_ACTOR_NAME;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_CATEGORY;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_CONTENT;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_DISPLAY_NAME;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_ENTITY_TYPE;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_OBJECT;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_OBJECT_ENTITY_TYPE;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_OBJECT_NAME;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_OBJECT_TYPE;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_TITLE;
import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_VERB;
import static org.apache.usergrid.security.AuthPrincipalType.ADMIN_USER;
import static org.apache.usergrid.security.AuthPrincipalType.APPLICATION;
import static org.apache.usergrid.security.AuthPrincipalType.APPLICATION_USER;
import static org.apache.usergrid.security.AuthPrincipalType.ORGANIZATION;
import static org.apache.usergrid.security.oauth.ClientCredentialsInfo.getTypeFromClientId;
import static org.apache.usergrid.security.oauth.ClientCredentialsInfo.getUUIDFromClientId;
import static org.apache.usergrid.security.tokens.TokenCategory.ACCESS;
import static org.apache.usergrid.security.tokens.TokenCategory.EMAIL;
import static org.apache.usergrid.services.ServiceParameter.parameters;
import static org.apache.usergrid.services.ServicePayload.payload;
import static org.apache.usergrid.services.ServiceResults.genericServiceResults;
import static org.apache.usergrid.utils.ClassUtils.cast;
import static org.apache.usergrid.utils.ConversionUtils.bytes;
import static org.apache.usergrid.utils.ConversionUtils.uuid;
import static org.apache.usergrid.utils.ListUtils.anyNull;
import static org.apache.usergrid.utils.MapUtils.hashMap;
import static org.apache.usergrid.utils.PasswordUtils.mongoPassword;

public class ManagementServiceImpl implements ManagementService {
    private static final Logger logger = LoggerFactory.getLogger(ManagementServiceImpl.class);

    /** 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";

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

    private static final String ORGANIZATION_PROPERTIES_DICTIONARY = "orgProperties";
    public static final String REGISTRATION_REQUIRES_ADMIN_APPROVAL = "registration_requires_admin_approval";
    public static final String REGISTRATION_REQUIRES_EMAIL_CONFIRMATION = "registration_requires_email_confirmation";
    public static final String NOTIFY_ADMIN_OF_NEW_USERS = "notify_admin_of_new_users";

    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);
    }

    /** For testing purposes only */
    public Properties getProperties() {
        return properties.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 (properties.getSuperUser().isEnabled()) {
            provisionSuperuser();
        }
    }

    @Override
    public void provisionSuperuser() throws Exception {
        final AccountCreationProps.SuperUser superUser = properties.getSuperUser();
        if (superUser.isEnabled()) {
            UserInfo user = this.getAdminUserByUsername(superUser.getUsername());
            if (user == null) {
                createAdminUser(superUser.getUsername(), "Super User", superUser.getEmail(),
                        superUser.getPassword(), superUser.isEnabled(), !superUser.isEnabled());
            } else {
                this.setAdminUserPassword(user.getUuid(), superUser.getPassword());
            }
        } else {
            System.out.println(
                    "Missing values for superuser account, check properties.  Skipping superuser account setup...");
        }
    }

    @Override
    public void resetSuperUser(String username, String password, String email) throws Exception {
        //final AccountCreationProps.SuperUser superUser = properties.getSuperUser();
        //this.getAdminUser
        UserInfo user = this.getAdminUserByUsername(username);
        if (user == null) {
            try {
                createAdminUser(username, "Super User", email, password, true, false);
            } catch (Exception e) {

            }
        } else {
            this.setAdminUserPassword(user.getUuid(), password);
        }
    }

    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<String, Object> properties = new HashMap<String, Object>();
        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<String, Object>() {
            {
                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<String, Object>() {
            {
                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<String, Object> userProperties,
            Map<String, Object> 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<String, Object> 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(), "<a href=\"mailto:" + user.getEmail() + "\">" + user.getName() + " ("
                        + user.getEmail() + ")</a> 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<String, Object> properties = organizationInfo.getProperties();
        if (properties != null) {
            EntityRef organizationEntity = new SimpleEntityRef(organizationInfo.getUuid());
            EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
            for (Map.Entry<String, Object> 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<String, Object> 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, Object>();
        }

        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.
     */
    private String buildAppName(String applicationName, OrganizationInfo organization) {
        return applicationName.contains("/") ? applicationName : organization.getName() + "/" + applicationName;
    }

    @Override
    public List<OrganizationInfo> getOrganizations(UUID startResult, int count) throws Exception {
        // still need the bimap to search for existing
        BiMap<UUID, String> organizations = HashBiMap.create();
        EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
        Results results = em.getCollection(em.getApplicationRef(), "groups", startResult, count,
                Level.ALL_PROPERTIES, false);
        List<OrganizationInfo> orgs = new ArrayList<OrganizationInfo>(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<UUID, String> getOrganizations() throws Exception {
        List<OrganizationInfo> orgs = getOrganizations(null, 10000);
        return buildOrgBiMap(orgs);
    }

    private BiMap<UUID, String> buildOrgBiMap(List<OrganizationInfo> orgs) {
        BiMap<UUID, String> 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<String, Object> properties = new HashMap<String, Object>();
        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.getConfirmed(), user.getActivated(), user.getDisabled(),
                user.getDynamicProperties());

        // special case for sysadmin and test account only
        if (!user.getEmail().equals(properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_EMAIL))
                && !user.getEmail().equals(properties.getProperty(PROPERTIES_TEST_ACCOUNT_ADMIN_USER_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<String, Object> 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<String, Object> 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("confirmed")),
                ConversionUtils.getBoolean(entity.getProperty("activated")),
                ConversionUtils.getBoolean(entity.getProperty("disabled")), entity.getDynamicProperties());
    }

    public UserInfo getUserInfo(UUID applicationId, Map<String, Object> properties) {

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

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

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

        List<UserInfo> users = new ArrayList<UserInfo>();

        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<String, Object> 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<String, Object> 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<String, CredentialsInfo> credsMap = cast(em.getDictionaryAsMap(user, CREDENTIALS_HISTORY));

        CredentialsInfo currentCredentials = null;
        if (passwordHistorySize > 0) {
            ArrayList<CredentialsInfo> oldCreds = new ArrayList<CredentialsInfo>(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<UUID> oldUUIDs = new ArrayList<UUID>(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);

            boolean userIsSuperAdmin = properties.getSuperUser().isEnabled()
                    && properties.getSuperUser().getEmail().equals(userInfo.getEmail());

            boolean testUserEnabled = parseBoolean(properties.getProperty(PROPERTIES_SETUP_TEST_ACCOUNT));
            boolean userIsTestUser = testUserEnabled
                    && properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_EMAIL).equals(userInfo.getEmail());

            if (!userIsSuperAdmin && !userIsTestUser) {

                if (!userInfo.isConfirmed() && newAdminUsersRequireConfirmation()) {
                    throw new UnconfirmedAdminUserException();
                }
                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<String, Object> 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.apache.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<UUID, String> getOrganizationsForAdminUser(UUID userId) throws Exception {

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

        BiMap<UUID, String> 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<String, Object> getAdminUserOrganizationData(UUID userId) throws Exception {
        UserInfo user = getAdminUserByUuid(userId);
        return getAdminUserOrganizationData(user, true);
    }

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

    @Override
    public Map<String, Object> getAdminUserOrganizationData(UserInfo user, boolean deep) throws Exception {

        Map<String, Object> json = new HashMap<String, Object>();

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

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

        Map<UUID, String> organizations;

        AccountCreationProps.SuperUser superUser = properties.getSuperUser();
        if (superUser.isEnabled() && superUser.getUsername().equals(user.getUsername())) {
            int maxOrganizations = this.getAccountCreationProps().getMaxOrganizationsForSuperUserLogin();
            organizations = buildOrgBiMap(getOrganizations(null, maxOrganizations));
        } else {
            organizations = getOrganizationsForAdminUser(user.getUuid());
        }

        for (Entry<UUID, String> organization : organizations.entrySet()) {
            Map<String, Object> jsonOrganization = new HashMap<String, Object>();

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

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

            if (deep) {
                BiMap<UUID, String> applications = getApplicationsForOrganization(organization.getKey());
                jsonOrganization.put("applications", applications.inverse());

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

        return json;
    }

    @Override
    public Map<String, Object> getOrganizationData(OrganizationInfo organization) throws Exception {

        Map<String, Object> jsonOrganization = new HashMap<String, Object>();
        jsonOrganization.putAll(JsonUtils.toJsonMap(organization));

        BiMap<UUID, String> applications = getApplicationsForOrganization(organization.getUuid());
        jsonOrganization.put("applications", applications.inverse());

        List<UserInfo> users = getAdminUsersForOrganization(organization.getUuid());
        Map<String, Object> jsonUsers = new HashMap<String, Object>();
        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<String, Object> properties) throws Exception {

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

        if (properties == null) {
            properties = new HashMap<String, Object>();
        }

        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, "<a href=\"mailto:" + user.getEmail() + "\">" + user.getName() + " ("
                            + user.getEmail() + ")</a> 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<UUID, String> getApplicationsForOrganization(UUID organizationId) throws Exception {

        if (organizationId == null) {
            return null;
        }
        final BiMap<UUID, String> applications = HashBiMap.create();
        final EntityManager em = emf.getEntityManager(MANAGEMENT_APPLICATION_ID);
        final Results results = em.getConnectedEntities(organizationId, "owns", APPLICATION_INFO,
                Level.ALL_PROPERTIES);
        final PagingResultsIterator itr = new PagingResultsIterator(results);

        String entityName;

        while (itr.hasNext()) {

            final Entity entity = (Entity) itr.next();

            entityName = entity.getName();

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

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

        return applications;
    }

    @Override
    public BiMap<UUID, String> getApplicationsForOrganizations(Set<UUID> organizationIds) throws Exception {
        if (organizationIds == null) {
            return null;
        }
        BiMap<UUID, String> applications = HashBiMap.create();
        for (UUID organizationId : organizationIds) {
            BiMap<UUID, String> 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<String, Object> 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"));
    }

    public String emailMsg(Map<String, String> 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<String, String> 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<UserInfo> 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<UserInfo> 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<UserInfo> 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", emailMsg(hashMap("confirmed_email", user.getEmail()),
                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<UserInfo> users = getAdminUsersForOrganization(organization.getUuid());
        for (UserInfo user : users) {
            boolean confirmed = user.isConfirmed() || !newAdminUsersRequireConfirmation();
            boolean shouldActivate = confirmed && !newAdminUsersRequireConfirmation();
            if (shouldActivate) {
                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.apache.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<String, String> 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();
    }

    @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();
    }

    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();
    }

    @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));
    }

    protected 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.apache.usergrid.management.ManagementService#setOrganizationProps(java.util
    * .UUID, java.util.Map)
    */
    @Override
    public void setOrganizationProps(UUID orgId, Map<String, Object> 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.apache.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 */
    protected void writeUserPassword(UUID appId, EntityRef owner, CredentialsInfo creds) throws Exception {
        writeCreds(appId, owner, creds, USER_PASSWORD);
    }

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

    /** Write the user's token */
    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 */
    protected CredentialsInfo readUserToken(UUID appId, UUID ownerId) throws Exception {
        return readCreds(appId, ownerId, USER_TOKEN);
    }

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

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

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

    /** Read the user's pin */
    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<CredentialsInfo> readUserPasswordHistory(UUID appId, UUID ownerId) throws Exception {
        EntityManager em = emf.getEntityManager(appId);
        Entity owner = em.get(ownerId);
        return (Set<CredentialsInfo>) 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;
    }

    @Override
    public Object registerAppWithAPM(OrganizationInfo orgInfo, ApplicationInfo appInfo) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }
}