/* * 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 com.google.common.base.Preconditions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.inject.Injector; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.text.StrSubstitutor; import org.apache.shiro.UnavailableSecurityManagerException; import org.apache.usergrid.corepersistence.service.AggregationService; import org.apache.usergrid.corepersistence.service.AggregationServiceFactory; import org.apache.usergrid.corepersistence.service.ApplicationService; import org.apache.usergrid.corepersistence.util.CpNamingUtils; import org.apache.usergrid.exception.ConflictException; import org.apache.usergrid.locking.Lock; import org.apache.usergrid.locking.LockManager; import org.apache.usergrid.management.*; import org.apache.usergrid.management.exceptions.*; import org.apache.usergrid.persistence.*; import org.apache.usergrid.persistence.Query.Level; import org.apache.usergrid.persistence.cache.CacheFactory; import org.apache.usergrid.persistence.cache.CacheScope; import org.apache.usergrid.persistence.cache.ScopedCache; import org.apache.usergrid.persistence.cassandra.CassandraService; import org.apache.usergrid.persistence.core.scope.ApplicationScope; 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.ApplicationAlreadyExistsException; import org.apache.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException; import org.apache.usergrid.persistence.exceptions.EntityNotFoundException; import org.apache.usergrid.persistence.index.query.Identifier; import org.apache.usergrid.persistence.model.entity.Id; import org.apache.usergrid.persistence.model.entity.SimpleId; import org.apache.usergrid.security.AuthPrincipalInfo; import org.apache.usergrid.security.AuthPrincipalType; import org.apache.usergrid.security.PasswordPolicy; 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.LocalShiroCache; 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.*; import org.apache.usergrid.services.exceptions.PasswordPolicyViolationException; import org.apache.usergrid.utils.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import rx.Observable; import java.nio.ByteBuffer; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; 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.*; import static org.apache.usergrid.management.OrganizationConfigProps.ORGPROPERTIES_ADMIN_SYSADMIN_EMAIL; import static org.apache.usergrid.management.OrganizationConfigProps.WorkflowUrl; import static org.apache.usergrid.persistence.CredentialsInfo.getCredentialsSecret; import static org.apache.usergrid.persistence.Schema.*; import static org.apache.usergrid.persistence.Schema.PROPERTY_UUID; import static org.apache.usergrid.persistence.entities.Activity.*; import static org.apache.usergrid.persistence.entities.Activity.PROPERTY_TITLE; import static org.apache.usergrid.security.AuthPrincipalType.*; 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 ORG_APP_RELATIONSHIP = "applications"; public static final String OAUTH_SECRET_SALT = "super secret oauth value"; private static final String ORGANIZATION_PROPERTIES_DICTIONARY = "orgProperties"; private static final String ORGANIZATION_CONFIG_DICTIONARY = "orgConfig"; 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"; public static final String ORG_CONFIG_CACHE_PROP = "usergrid.orgconfig.cache.timeout"; protected ServiceManagerFactory smf; protected EntityManagerFactory emf; protected AccountCreationPropsImpl properties; protected OrganizationConfigPropsImpl orgConfigProperties; protected LockManager lockManager; protected TokenService tokens; protected SaltProvider saltProvider; @Autowired protected MailUtils mailUtils; protected EncryptionService encryptionService; protected CacheFactory cacheFactory; protected AggregationServiceFactory aggregationServiceFactory; protected ApplicationService service; protected LocalShiroCache localShiroCache; protected PasswordPolicy passwordPolicy; private LoadingCache<UUID, OrganizationConfig> orgConfigByAppCache = CacheBuilder.newBuilder().maximumSize( 1000 ) .expireAfterWrite( Long.valueOf( System.getProperty(ORG_CONFIG_CACHE_PROP, "30000") ) , TimeUnit.MILLISECONDS) .build( new CacheLoader<UUID, OrganizationConfig>() { public OrganizationConfig load( UUID applicationInfoId ) { try { if (applicationInfoId != null && applicationInfoId != CpNamingUtils.MANAGEMENT_APPLICATION_ID) { final EntityManager em = emf.getEntityManager(smf.getManagementAppId()); Results r = em.getSourceEntities( new SimpleEntityRef(CpNamingUtils.APPLICATION_INFO, applicationInfoId), ORG_APP_RELATIONSHIP, Group.ENTITY_TYPE, Level.ALL_PROPERTIES); Group org = (Group) r.getEntity(); if (org != null) { Map<Object, Object> entityProperties = em.getDictionaryAsMap(org, ORGANIZATION_CONFIG_DICTIONARY); return new OrganizationConfig(orgConfigProperties, org.getUuid(), org.getPath(), entityProperties, false); } } return new OrganizationConfig(orgConfigProperties); } catch (Exception e) { return new OrganizationConfig(orgConfigProperties); } }} ); /** Must be constructed with a CassandraClientPool. */ public ManagementServiceImpl(Injector injector) { // Use the injector to get our guice dependencies this.lockManager = injector.getInstance(LockManager.class); this.cacheFactory = injector.getInstance( CacheFactory.class ); this.aggregationServiceFactory = injector.getInstance(AggregationServiceFactory.class); this.service = injector.getInstance(ApplicationService.class); this.localShiroCache = injector.getInstance(LocalShiroCache.class); this.passwordPolicy = injector.getInstance( PasswordPolicy.class ); } @Autowired public void setEntityManagerFactory( EntityManagerFactory emf ) { logger.info( "ManagementServiceImpl.setEntityManagerFactory" ); this.emf = emf; } @Autowired public void setProperties( Properties properties ) { this.properties = new AccountCreationPropsImpl( properties ); this.orgConfigProperties = new OrganizationConfigPropsImpl( properties ); } String orgSysAdminEmail,defaultSysAdminEmail; private String getDefaultSysAdminEmail(){ defaultSysAdminEmail = defaultSysAdminEmail != null ? defaultSysAdminEmail : properties.getProperty(PROPERTIES_DEFAULT_SYSADMIN_EMAIL); return defaultSysAdminEmail; } private String getOrgSystemEmail(){ if( orgSysAdminEmail != null ){ return orgSysAdminEmail; } orgSysAdminEmail = properties.getProperty( PROPERTIES_ORG_SYSADMIN_EMAIL ); if (orgSysAdminEmail == null || orgSysAdminEmail.isEmpty()) { orgSysAdminEmail = getDefaultSysAdminEmail(); } return orgSysAdminEmail; } String defaultAdminSysAdminEmail = null; private String getDefaultAdminSystemEmail(){ if( defaultAdminSysAdminEmail == null ){ defaultAdminSysAdminEmail = properties.getProperty(ORGPROPERTIES_ADMIN_SYSADMIN_EMAIL); if (defaultAdminSysAdminEmail == null || defaultAdminSysAdminEmail.isEmpty()) { defaultAdminSysAdminEmail = getDefaultSysAdminEmail(); } } return defaultAdminSysAdminEmail; } private String getAdminSystemEmailForApplication(UUID applicationId) { String adminSystemEmail = null; try { OrganizationConfig orgConfig = getOrganizationConfigForApplication(applicationId); if (orgConfig != null) { adminSystemEmail = orgConfig.getProperty(ORGPROPERTIES_ADMIN_SYSADMIN_EMAIL); } } catch (Exception e) { // swallow } return (adminSystemEmail != null && !adminSystemEmail.isEmpty()) ? adminSystemEmail : getDefaultAdminSystemEmail(); } private String getAdminSystemEmailForOrganization(UUID organizationId) { String adminSystemEmail = null; try { OrganizationConfig orgConfig = getOrganizationConfigByUuid(organizationId); if (orgConfig != null) { adminSystemEmail = orgConfig.getProperty(ORGPROPERTIES_ADMIN_SYSADMIN_EMAIL); } } catch (Exception e) { // swallow } return adminSystemEmail != null ? adminSystemEmail : getDefaultAdminSystemEmail(); } /** 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; } /** @param encryptionService the encryptionService to set */ @Autowired public void setEncryptionService( EncryptionService encryptionService ) { this.encryptionService = encryptionService; } @Override public void setup() throws Exception { if ( getBooleanProperty( 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 ) ) { try { createApplication( organization.getUuid(), test_app_name ); }catch(ApplicationAlreadyExistsException aaee){ if(logger.isDebugEnabled()){ logger.debug("The database setup already found an existing application"); } } } } else { logger.warn( "Test app creation disabled" ); } if ( superuserEnabled() ) { provisionSuperuser(); } } public boolean superuserEnabled() { boolean superuser_enabled = getBooleanProperty( PROPERTIES_SYSADMIN_LOGIN_ALLOWED ); String superuser_username = properties.getProperty( PROPERTIES_SYSADMIN_LOGIN_NAME ); String superuser_email = properties.getProperty( PROPERTIES_SYSADMIN_LOGIN_EMAIL ); String superuser_password = properties.getProperty( PROPERTIES_SYSADMIN_LOGIN_PASSWORD ); return superuser_enabled && !anyNull( superuser_username, superuser_email, superuser_password ); } @Override public void provisionSuperuser() throws Exception { boolean superuser_enabled = getBooleanProperty( PROPERTIES_SYSADMIN_LOGIN_ALLOWED ); String superuser_username = properties.getProperty( PROPERTIES_SYSADMIN_LOGIN_NAME ); String superuser_email = properties.getProperty( PROPERTIES_SYSADMIN_LOGIN_EMAIL ); String superuser_password = properties.getProperty(PROPERTIES_SYSADMIN_LOGIN_PASSWORD); if ( !anyNull( superuser_username, superuser_email, superuser_password ) ) { UserInfo user = this.getAdminUserByUsername( superuser_username ); if ( user == null ) { createAdminUser( null, superuser_username, "Super User", superuser_email, superuser_password, superuser_enabled, !superuser_enabled ); } else { this.setAdminUserPassword( user.getUuid(), superuser_password ); } } 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( null, username, "Super User", email, password, true, false ); }catch(Exception e){ logger.info("resetSuperUser: auto creation of superuser failed: {}", e.getMessage()); } } 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() ) ); return type.getBase64Prefix() + encodeBase64URLSafeString( bytes.array() ); } @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( smf.getManagementAppId() ); Map<String, Object> properties = new HashMap<>(); properties.put( PROPERTY_VERB, verb ); properties.put( PROPERTY_CATEGORY, "admin" ); if ( content != null ) { properties.put( PROPERTY_CONTENT, content ); } if ( title != null ) { properties.put( PROPERTY_TITLE, title ); } properties.put( PROPERTY_ACTOR, new HashMap<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( Schema.COLLECTION_GROUPS, organizationId, "activities" ), payload( properties ) ).execute().getEntity(); } @Override public ServiceResults getOrganizationActivity( OrganizationInfo organization ) throws Exception { ServiceManager sm = smf.getServiceManager( smf.getManagementAppId() ); return sm.newRequest( ServiceAction.GET, parameters( Schema.COLLECTION_GROUPS, organization.getUuid(), "feed" ) ).execute(); } @Override public ServiceResults getOrganizationActivityForAdminUser( OrganizationInfo organization, UserInfo user ) throws Exception { ServiceManager sm = smf.getServiceManager( smf.getManagementAppId() ); return sm.newRequest( ServiceAction.GET, parameters( Schema.COLLECTION_GROUPS, organization.getUuid(), "users", user.getUuid(), "feed" ) ).execute(); } @Override public ServiceResults getAdminUserActivity( UserInfo user ) throws Exception { ServiceManager sm = smf.getServiceManager(smf.getManagementAppId()); 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 { if(logger.isTraceEnabled()){ logger.trace("createOwnerAndOrganization(1)"); } 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 { if(logger.isTraceEnabled()){ logger.trace("createOwnerAndOrganization(2)"); } 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 { logger.info("createOwnerAndOrganization: {} {}", organizationName, email); /** * 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, smf.getManagementAppId(), organizationName, Schema.COLLECTION_GROUPS, PROPERTY_PATH ); Lock userLock = getUniqueUpdateLock( lockManager, smf.getManagementAppId(), username, "users", "username" ); Lock emailLock = getUniqueUpdateLock( lockManager, smf.getManagementAppId(), email, "users", "email" ); UserInfo user = null; OrganizationInfo organization = null; try { groupLock.lock(); userLock.lock(); emailLock.lock(); EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); if ( !em.isPropertyValueUniqueForEntity( Group.ENTITY_TYPE, PROPERTY_PATH, organizationName ) ) { throw new DuplicateUniquePropertyExistsException( Group.ENTITY_TYPE, PROPERTY_PATH, organizationName ); } if ( !validateAdminInfo( username, name, email, password ) ) { return null; } // sysadmin can omit password field in the request and that will try to fetch an existing admin user to // associate to the requested organization if((password == null || password.isEmpty()) && SubjectUtils.isServiceAdmin()){ user = getAdminUserByEmail(email); if(user == null && !tokens.isExternalSSOProviderEnabled() ){ throw new IllegalArgumentException("Password should be sent in the request or should be a valid admin user email."); } } if(user == null) { // if external SSO is enabled and we're adding a user to an org, auto activate the user if (tokens.isExternalSSOProviderEnabled() || areActivationChecksDisabled()) { user = createAdminUser(null, username, name, email, password, true, false, userProperties); } else { user = createAdminUser(null, username, name, email, password, activated, disabled, userProperties); } } if(logger.isTraceEnabled()){ logger.debug("createOwnerAndOrganization: User created"); } organization = createOrganizationInternal( null, organizationName, user, true, organizationProperties ); } finally { emailLock.unlock(); userLock.unlock(); groupLock.unlock(); } return new OrganizationOwnerInfo( user, organization ); } private OrganizationInfo createOrganizationInternal( UUID orgUuid, String organizationName, UserInfo user, boolean activated ) throws Exception { if(logger.isTraceEnabled()){ logger.trace("createOrganizationInternal(1): {}", organizationName); } return createOrganizationInternal( orgUuid, organizationName, user, activated, null ); } private OrganizationInfo createOrganizationInternal( UUID orgUuid, String organizationName, UserInfo user, boolean activated, Map<String, Object> properties ) throws Exception { if(logger.isTraceEnabled()){ logger.trace( "createOrganizationInternal(2): {}", organizationName ); } if ( organizationName == null ) { if(logger.isDebugEnabled()){ logger.debug("organizationName = null"); } return null; } if ( user == null ) { if(logger.isDebugEnabled()){ logger.debug("user = null (organizationName = {})", organizationName); } return null; } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); Group organizationEntity = new Group(); organizationEntity.setPath( organizationName ); if ( orgUuid == null ) { organizationEntity = em.create( organizationEntity ); } else { em.create( orgUuid, Group.ENTITY_TYPE, organizationEntity.getProperties() ); organizationEntity = em.get( orgUuid, Group.class ); } em.addToCollection( organizationEntity, "users", new SimpleEntityRef( User.ENTITY_TYPE, user.getUuid() ) ); // em.addToCollection( new SimpleEntityRef( User.ENTITY_TYPE, user.getUuid() ), Schema.COLLECTION_GROUPS, organizationEntity ); writeUserToken( smf.getManagementAppId(), organizationEntity, encryptionService .plainTextCredentials( generateOAuthSecretKey( AuthPrincipalType.ORGANIZATION ), user.getUuid(), smf.getManagementAppId() ) ); OrganizationInfo organization = new OrganizationInfo( organizationEntity.getUuid(), organizationName, properties ); updateOrganization( organization ); if ( orgUuid == null ) { // no import ID specified, so do the activation email flow stuff 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 { return createOrganization( null, organizationName, user, activated ); } @Override public OrganizationInfo createOrganization(UUID orgUuid, String organizationName, UserInfo user, boolean activated) throws Exception { if ( organizationName == null ) { if (logger.isTraceEnabled()) { logger.trace("organizationName = null"); } return null; } if ( user == null ) { if (logger.isTraceEnabled()) { logger.trace("user = null"); } return null; } Lock groupLock = getUniqueUpdateLock( lockManager, smf.getManagementAppId(), organizationName, Schema.COLLECTION_GROUPS, PROPERTY_PATH ); EntityManager em = emf.getEntityManager(smf.getManagementAppId()); if ( !em.isPropertyValueUniqueForEntity( Group.ENTITY_TYPE, PROPERTY_PATH, organizationName ) ) { throw new DuplicateUniquePropertyExistsException( Group.ENTITY_TYPE, PROPERTY_PATH, organizationName ); } try { groupLock.lock(); return createOrganizationInternal( orgUuid, 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( Group.ENTITY_TYPE, organizationInfo.getUuid() ); EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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 { 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 && organizationInfo != null) { organizationId = organizationInfo.getUuid(); } if ( organizationId == null ) { organizationId = uuid( properties.get( PROPERTY_UUID ) ); } if ( organizationId == null ) { return null; } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); if ( !em.isPropertyValueUniqueForEntity( Group.ENTITY_TYPE, PROPERTY_PATH, organizationName ) ) { throw new DuplicateUniquePropertyExistsException( Group.ENTITY_TYPE, PROPERTY_PATH, organizationName ); } if ( properties == null ) { properties = new HashMap<>(); } 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, final Application application ) throws Exception { throw new UnsupportedOperationException("Import application not supported"); } /** * 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 org.apache.commons.lang.StringUtils.lowerCase( 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(smf.getManagementAppId()); Results results = em.getCollection(em.getApplicationRef(), Schema.COLLECTION_GROUPS, startResult, count, Level.ALL_PROPERTIES, false); List<OrganizationInfo> orgs = new ArrayList<>( results.size() ); OrganizationInfo orgInfo; for ( Entity entity : results.getEntities() ) { String path = ( String ) entity.getProperty( PROPERTY_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(smf.getManagementAppId()); EntityRef ref = em.getAlias( Group.ENTITY_TYPE, organizationName ); if ( ref == null ) { return null; } return getOrganizationByUuid( ref.getUuid() ); } @Override public OrganizationInfo getOrganizationByUuid( UUID id ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); Entity entity = em.get( new SimpleEntityRef( Group.ENTITY_TYPE, id ) ); if ( entity == null ) { return null; } Map<Object, Object> 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( smf.getManagementAppId() ); Map<String, Object> properties = new HashMap<>(); properties.put( PROPERTY_VERB, verb ); properties.put( PROPERTY_CATEGORY, "admin" ); if ( content != null ) { properties.put( PROPERTY_CONTENT, content ); } if ( title != null ) { properties.put( PROPERTY_TITLE, title ); } properties.put( PROPERTY_ACTOR, user.getUuid() ); properties.put( PROPERTY_ACTOR_NAME, user.getName() ); properties.put( PROPERTY_OBJECT, object.getUuid() ); properties.put( PROPERTY_OBJECT_ENTITY_TYPE, object.getType() ); properties.put( PROPERTY_OBJECT_TYPE, objectType ); properties.put( PROPERTY_OBJECT_NAME, objectName ); sm.newRequest( ServiceAction.POST, parameters( "users", user.getUuid(), "activities" ), payload( properties ) ) .execute().getEntity(); } @Override public ServiceResults getAdminUserActivities( UserInfo user ) throws Exception { ServiceManager sm = smf.getServiceManager( smf.getManagementAppId() ); ServiceRequest request = sm.newRequest( ServiceAction.GET, parameters( "users", user.getUuid(), "feed" ) ); return request.execute(); } private UserInfo doCreateAdmin( UUID organizationId, User user, CredentialsInfo userPassword, CredentialsInfo mongoPassword ) throws Exception { writeUserToken( smf.getManagementAppId(), user, encryptionService .plainTextCredentials( generateOAuthSecretKey( AuthPrincipalType.ADMIN_USER ), user.getUuid(), smf.getManagementAppId() ) ); writeUserPassword( smf.getManagementAppId(), user, userPassword ); writeUserMongoPassword( smf.getManagementAppId(), user, mongoPassword ); UserInfo userInfo = new UserInfo( smf.getManagementAppId(), user.getUuid(), user.getUsername(), user.getName(), user.getEmail(), user.getConfirmed(), user.getActivated(), user.getDisabled(), user.getDynamicProperties(), true ); // 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 ) ) ) { if(!tokens.isExternalSSOProviderEnabled()) { this.startAdminUserActivationFlow(organizationId, userInfo, false); } } return userInfo; } @Override public UserInfo createAdminFromPrexistingPassword( UUID organizationId, User user, CredentialsInfo ci ) throws Exception { return doCreateAdmin( organizationId, 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(), smf.getManagementAppId() ) ); } @Override public UserInfo createAdminFrom( UUID organizationId, User user, String password ) throws Exception { Collection<String> policyVioliations = passwordPolicy.policyCheck( password, true ); if ( !policyVioliations.isEmpty() ) { throw new PasswordPolicyViolationException( passwordPolicy.getDescription( true ), policyVioliations ); } return doCreateAdmin(organizationId, user, encryptionService.defaultEncryptedCredentials(password, user.getUuid(), smf.getManagementAppId()), encryptionService.plainTextCredentials(mongoPassword(user.getUsername(), password), user.getUuid(), smf.getManagementAppId())); } @Override public UserInfo createAdminUser( UUID organizationId, String username, String name, String email, String password, boolean activated, boolean disabled ) throws Exception { return createAdminUser(organizationId, username, name, email, password, activated, disabled, null); } @Override public UserInfo createAdminUser( UUID organizationId, 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( organizationId, username, name, email, password, activated, disabled, userProperties ); } protected boolean validateAdminInfo( String username, String name, String email, String password ) throws Exception { if ( email == null ) { return false; } if ( username == null ) { username = email; } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); if ( !( tokens.isExternalSSOProviderEnabled() && SubjectUtils.isServiceAdmin()) && !em.isPropertyValueUniqueForEntity( "user", "username", username ) ) { throw new DuplicateUniquePropertyExistsException( "user", "username", username ); } if ( !(tokens.isExternalSSOProviderEnabled()&& SubjectUtils.isServiceAdmin()) && !em.isPropertyValueUniqueForEntity( "user", "email", email ) ) { throw new DuplicateUniquePropertyExistsException( "user", "email", email ); } return true; } protected UserInfo createAdminUserInternal( UUID organizationId, String username, String name, String email, String password, boolean activated, boolean disabled, Map<String, Object> userProperties ) throws Exception { logger.debug( "createAdminUserInternal - username: {}, email: {}, name: {}", username, email, name ); if ( isBlank( password ) ) { password = encodeBase64URLSafeString( bytes( UUID.randomUUID() ) ); } if ( username == null ) { username = email; } if ( name == null ) { name = email; } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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( organizationId, 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(), ConversionUtils.getBoolean( entity.getProperty( "admin" ))); } 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<>(); EntityManager em = emf.getEntityManager(smf.getManagementAppId()); 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( smf.getManagementAppId(), 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, smf.getManagementAppId(), username, "users", "username" ); Lock emailLock = getUniqueUpdateLock( lockManager, smf.getManagementAppId(), email, "users", "email" ); try { usernameLock.lock(); emailLock.lock(); EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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 ); Entity entity = em.get( entityRef, User.class); em.updateProperties( entity, 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(smf.getManagementAppId(), Identifier.fromEmail(email)); } @Override public UserInfo getAdminUserByEmail( String email ) throws Exception { if ( email == null ) { return null; } return getUserInfo(smf.getManagementAppId(), getUserEntityByIdentifier(smf.getManagementAppId(), Identifier.fromEmail(email))); } public User getUserEntityByIdentifier( UUID applicationId, Identifier identifier ) throws Exception { EntityManager em = emf.getEntityManager( applicationId ); return em.get(em.getUserByIdentifier(identifier), User.class); } @Override public UserInfo getAdminUserByUsername( String username ) throws Exception { if ( username == null ) { return null; } return getUserInfo(smf.getManagementAppId(), getUserEntityByIdentifier(smf.getManagementAppId(), Identifier.fromName(username))); } @Override public User getAdminUserEntityByUuid( UUID id ) throws Exception { if ( id == null ) { return null; } return getUserEntityByIdentifier(smf.getManagementAppId(), Identifier.fromUUID(id)); } @Override public UserInfo getAdminUserByUuid( UUID id ) throws Exception { return getUserInfo(smf.getManagementAppId(), getUserEntityByIdentifier(smf.getManagementAppId(), Identifier.fromUUID(id))); } @Override public User getAdminUserEntityByIdentifier( Identifier id ) throws Exception { return getUserEntityByIdentifier( smf.getManagementAppId(), 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 identifierString ) { User user = null; if ( UUIDUtils.isUUID( identifierString ) ) { try { Entity entity = getUserEntityByIdentifier( applicationId, Identifier.fromUUID( UUID.fromString( identifierString ) ) ); if ( entity != null ) { user = ( User ) entity.toTypedEntity(); if (logger.isTraceEnabled()) { logger.trace("Found user {} as a UUID", identifierString); } } } catch ( Exception e ) { if (logger.isTraceEnabled()) { logger.trace("Unable to get user {} as a UUID, trying username...", identifierString); } } return user; } // now we are either an email or a username. Let Indentifier handle the parsing of such. Identifier identifier = Identifier.from( identifierString ); try { Entity entity = getUserEntityByIdentifier( applicationId, identifier ); if ( entity != null ) { user = ( User ) entity.toTypedEntity(); if (logger.isTraceEnabled()) { logger.trace("Found user {} as an {}", identifierString, identifier.getType()); } } } catch ( Exception e ) { logger.warn( "Unable to get user {} as a {}", identifierString, identifier.getType(), e); } if ( user != null ) { return user; } return null; } @Override public UserInfo findAdminUser( String identifier ) { return getUserInfo( smf.getManagementAppId(), findUserEntity( smf.getManagementAppId(), 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( smf.getManagementAppId() ).get( userId, User.class ); if ( !verify( smf.getManagementAppId(), user.getUuid(), oldPassword ) ) { 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; } Collection<String> policyVioliations = passwordPolicy.policyCheck( newPassword, true ); if ( !policyVioliations.isEmpty() ) { throw new PasswordPolicyViolationException( passwordPolicy.getDescription( true ), policyVioliations ); } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); User user = em.get( userId, User.class ); CredentialsInfo newCredentials = encryptionService.defaultEncryptedCredentials( newPassword, user.getUuid(), smf.getManagementAppId() ); 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<>( credsMap.values() ); Collections.sort( oldCreds ); currentCredentials = readUserPasswordCredentials( smf.getManagementAppId(), user.getUuid(), user.getType() ); // check credential history if ( encryptionService.verify( newPassword, currentCredentials, userId, smf.getManagementAppId() ) ) { throw new RecentlyUsedPasswordException(); } for ( int i = 0; i < oldCreds.size() && i < passwordHistorySize; i++ ) { CredentialsInfo ci = oldCreds.get( i ); if ( encryptionService.verify( newPassword, ci, userId, smf.getManagementAppId() ) ) { throw new RecentlyUsedPasswordException(); } } } // remove excess history if ( credsMap.size() > passwordHistorySize ) { ArrayList<UUID> oldUUIDs = new ArrayList<>( credsMap.size() ); credsMap.keySet().forEach((uuid) -> 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( smf.getManagementAppId(), user, newCredentials ); writeUserMongoPassword( smf.getManagementAppId(), user, encryptionService .plainTextCredentials( mongoPassword( ( String ) user.getProperty( "username" ), newPassword ), user.getUuid(), smf.getManagementAppId() ) ); } public int calculatePasswordHistorySizeForUser( UUID userId ) throws Exception { if(logger.isTraceEnabled()){ logger.trace( "calculatePasswordHistorySizeForUser {}", userId.toString() ); } int size = 0; EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); Results orgResults = em.getCollection( new SimpleEntityRef( User.ENTITY_TYPE, userId ), Schema.COLLECTION_GROUPS, null, 10000, Level.REFS, false ); if(logger.isTraceEnabled()){ logger.trace(" orgResults.size() = {}", orgResults.size()); } for ( EntityRef orgRef : orgResults.getRefs() ) { Map<Object, Object> properties = em.getDictionaryAsMap( orgRef, ORGANIZATION_PROPERTIES_DICTIONARY ); if ( properties != null ) { OrganizationInfo orgInfo = new OrganizationInfo( null, null, properties ); if(logger.isTraceEnabled()){ logger.trace( " orgInfo.getPasswordHistorySize() = {}", orgInfo.getPasswordHistorySize() ); } size = Math.max( orgInfo.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( smf.getManagementAppId() ).get( userId, User.class ); return verify( smf.getManagementAppId(), user.getUuid(), password ); } @Override public UserInfo verifyAdminUserPasswordCredentials( String name, String password ) throws Exception { if(logger.isTraceEnabled()){ logger.trace("verifyAdminUserPasswordCredentials for {}", name); } User user = findUserEntity( smf.getManagementAppId(), name ); if ( user == null ) { return null; } if ( verify( smf.getManagementAppId(), user.getUuid(), password ) ) { UserInfo userInfo = getUserInfo( smf.getManagementAppId(), 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( smf.getManagementAppId(), name ); if ( user == null ) { return null; } String mongo_pwd = readUserMongoPassword( smf.getManagementAppId(), user.getUuid(), user.getType() ).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( smf.getManagementAppId(), user.getProperties() ); if ( !userInfo.isActivated() ) { throw new UnactivatedAdminUserException(); } if ( userInfo.isDisabled() ) { throw new DisabledAdminUserException(); } return userInfo; } public String getTokenForPrincipal( TokenCategory token_category, String token_type, UUID applicationId, AuthPrincipalType principal_type, UUID id, long duration ) throws Exception { return getTokenForPrincipal(token_category, token_type, applicationId, principal_type, id, duration, null); } // include workflowOrgId public String getTokenForPrincipal( TokenCategory token_category, String token_type, UUID applicationId, AuthPrincipalType principal_type, UUID id, long duration, UUID workflowOrgId) 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, workflowOrgId ); } 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 boolean validateTokenAndPrincipalTypes(TokenInfo tokenInfo, String expected_token_type, AuthPrincipalType expected_principal_type) throws Exception { boolean success = true; if (tokenInfo == null || (expected_token_type != null && !expected_token_type.equals(tokenInfo.getType()))) { success = false; } else { AuthPrincipalInfo principal = tokenInfo.getPrincipal(); if (principal == null || (expected_principal_type != null && !expected_principal_type.equals(principal.getType()))) { success = false; } } return success; } public TokenInfo getTokenInfoFromAccessToken(String token, String expected_token_type, AuthPrincipalType expected_principal_type) throws Exception { return getTokenInfoFromAccessToken(token, expected_token_type, expected_principal_type, true); } public TokenInfo getTokenInfoFromAccessToken(String token, String expected_token_type, AuthPrincipalType expected_principal_type, boolean updateAccessTime) throws Exception { TokenInfo tokenInfo = tokens.getTokenInfo( token, updateAccessTime ); return validateTokenAndPrincipalTypes(tokenInfo, expected_token_type, expected_principal_type) ? tokenInfo : null; } public AuthPrincipalInfo getPrincipalFromAccessToken(String token, String expected_token_type, AuthPrincipalType expected_principal_type) throws Exception { // validates expected types TokenInfo tokenInfo = getTokenInfoFromAccessToken(token, expected_token_type, expected_principal_type); return tokenInfo != null ? tokenInfo.getPrincipal() : null; } 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() : smf.getManagementAppId() ); return em.get(new SimpleEntityRef( principal.getType().getEntityType(), principal.getUuid())); } @Override public String getAccessTokenForAdminUser( UUID userId, long duration ) throws Exception { return getTokenForPrincipal( ACCESS, null, smf.getManagementAppId(), 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, smf.getManagementAppId(), 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 { return getEntityFromAccessToken(token, null, ADMIN_USER); } @Override public UserInfo getAdminUserInfoFromAccessToken( String token ) throws Exception { Entity user = getAdminUserEntityFromAccessToken(token); return new UserInfo( smf.getManagementAppId(), 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( smf.getManagementAppId() ); EntityRef userRef = new SimpleEntityRef(User.ENTITY_TYPE, userId); Results results = em.getCollection( userRef, Schema.COLLECTION_GROUPS, null, 1000, Level.ALL_PROPERTIES, false ); do { for ( Entity entity : results.getEntities() ) { String path = ( String ) entity.getProperty( PROPERTY_PATH ); if ( path != null ) { path = path.toLowerCase(); } // check that user is in users collection for org EntityRef groupRef = new SimpleEntityRef(Group.ENTITY_TYPE, entity.getUuid()); if (em.isCollectionMember(groupRef, Schema.COLLECTION_USERS, userRef)) { try { organizations.put(entity.getUuid(), path); } catch (IllegalArgumentException e) { logger.warn("Error adding {}:{} to BiMap: {}", entity.getUuid(), path, e.getMessage()); } } else { // org doesn't know about user, so read repair em.removeFromCollection(userRef, Schema.COLLECTION_GROUPS, groupRef); } } results = results.hasMoreResults() ? results.getNextPageResults() : null ; } while (results != null); return organizations; } @Override public Map<String, Object> getAdminUserOrganizationData( UUID userId ) throws Exception { UserInfo user = getAdminUserByUuid( userId ); return getAdminUserOrganizationData( user, true, true); } @Override public Long getLastAdminPasswordChange( UUID userId ) throws Exception { CredentialsInfo ci = readUserPasswordCredentials(smf.getManagementAppId(), userId, User.ENTITY_TYPE); return ci.getCreated(); } @Override public Map<String, Object> getAdminUserOrganizationData(UserInfo user, boolean includeApps, boolean includeOrgUsers) throws Exception { Map<String, Object> json = new HashMap<>(); json.putAll( JsonUtils.toJsonMap( user ) ); Map<String, Map<String, Object>> jsonOrganizations = new HashMap<>(); 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<>(); 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 ( includeApps ) { BiMap<UUID, String> applications = getApplicationsForOrganization(organization.getKey()); jsonOrganization.put("applications", applications.inverse()); } if ( includeOrgUsers ){ List<UserInfo> users = getAdminUsersForOrganization( organization.getKey() ); Map<String, Object> jsonUsers = new HashMap<>(); 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<>(); 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<>(); 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( smf.getManagementAppId() ); EntityRef orgRef = new SimpleEntityRef( Group.ENTITY_TYPE, organization.getUuid() ); EntityRef userRef = new SimpleEntityRef( User.ENTITY_TYPE, user.getUuid() ); if(em.isCollectionMember(orgRef, Schema.COLLECTION_USERS, userRef)) { if(logger.isDebugEnabled()) { logger.debug( "addAdminUserToOrganization - Found value: {} already in collection", user.getName() ); } return; } em.addToCollection(orgRef, Schema.COLLECTION_USERS, userRef); invalidateManagementAppAuthCache(); if ( email ) { sendAdminUserInvitedEmail(user, organization); } } @Override public void removeAdminUserFromOrganization( UUID userId, UUID organizationId ) throws Exception { removeAdminUserFromOrganization( userId, organizationId, false ); } @Override public void removeAdminUserFromOrganization( UUID userId, UUID organizationId, boolean force ) throws Exception { if ( ( userId == null ) || ( organizationId == null ) ) { return; } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); try { Results collection = em.getCollection( new SimpleEntityRef( Group.ENTITY_TYPE, organizationId ), "users", null, 2, Level.IDS, false ); int size = collection.size(); if ( !force && (size == 0 || (size == 1 && collection.getId() == userId))) { 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)); invalidateManagementAppAuthCache(); } @Override public ApplicationInfo createApplication( UUID organizationId, String applicationName ) throws Exception { // DO NOT CHANGE THIS AS SOME EXTERNAL CLASSES MAY RELY ON THIS BEHAVIOR WHEN EXTENDING return createApplication( organizationId, applicationName, null ); } @Override public ApplicationInfo createApplication( UUID organizationId, String applicationName, Map<String, Object> properties ) throws Exception { return createApplication(organizationId, applicationName, null, properties, false); } @Override public ApplicationInfo createApplication(UUID organizationId, String applicationName, UUID applicationId, Map<String, Object> properties, boolean forMigration) throws Exception { if ( ( organizationId == null ) || ( applicationName == null ) ) { return null; } if ( properties == null ) { properties = new HashMap<>(); } OrganizationInfo organizationInfo = getOrganizationByUuid( organizationId ); Entity appInfo = emf.createApplicationV2( organizationInfo.getName(), applicationName, applicationId ,properties, forMigration); // only generate a client secret on app creation when the app is not being created during appinfo migration if( !forMigration ){ writeUserToken( smf.getManagementAppId(), appInfo, encryptionService.plainTextCredentials( generateOAuthSecretKey( AuthPrincipalType.APPLICATION ), null, smf.getManagementAppId() ) ); } applicationId = addApplicationToOrganization( organizationId, appInfo ); UserInfo user = null; try { user = SubjectUtils.getUser(); } catch ( UnavailableSecurityManagerException e ) { // occurs in the rare case that this is called before the full stack is initialized logger.warn("Error getting user, application created activity will not be created", e); } if ( ( user != null ) && user.isAdminUser() ) { postOrganizationActivity( organizationId, user, "create", appInfo, "Application", applicationName, "<a href=\"mailto:" + user.getEmail() + "\">" + user.getName() + " (" + user.getEmail() + ")</a> created a new application named " + applicationName, null ); } invalidateManagementAppAuthCache(); return new ApplicationInfo( applicationId, appInfo.getName() ); } @Override public void deleteApplication(UUID applicationId) throws Exception { emf.deleteApplication( applicationId ); } @Override public ApplicationInfo restoreApplication(UUID applicationId) throws Exception { ApplicationInfo app = getDeletedApplicationInfo( applicationId ); if ( app == null ) { throw new EntityNotFoundException("Deleted application ID " + applicationId + " not found"); } if ( emf.lookupApplication( app.getName() ) != null ) { throw new ConflictException("Cannot restore application, one with that name already exists."); } // restore application_info entity EntityManager em = emf.getEntityManager( emf.getManagementAppId() ); Entity appInfo = emf.restoreApplication(applicationId); // restore token writeUserToken( smf.getManagementAppId(), appInfo, encryptionService.plainTextCredentials( generateOAuthSecretKey( AuthPrincipalType.APPLICATION ), null, smf.getManagementAppId() ) ); String orgName = appInfo.getName().split("/")[0]; EntityRef alias = em.getAlias( Group.ENTITY_TYPE, orgName ); Entity orgEntity = em.get( alias ); addApplicationToOrganization( orgEntity.getUuid(), appInfo ); // create activity UserInfo user = null; try { user = SubjectUtils.getUser(); } catch ( UnavailableSecurityManagerException e ) { // occurs in the rare case that this is called before the full stack is initialized logger.warn("Error getting user, application restored created activity will not be created", e); } if ( ( user != null ) && user.isAdminUser() ) { postOrganizationActivity( orgEntity.getUuid(), user, "restore", appInfo, "Application", appInfo.getName(), "<a href=\"mailto:" + user.getEmail() + "\">" + user.getName() + " (" + user.getEmail() + ")</a> restored an application named " + appInfo.getName(), null ); } invalidateManagementAppAuthCache(); return new ApplicationInfo( applicationId, appInfo.getName() ); } @Override public long getApplicationSize(final UUID applicationId) { AggregationService aggregationService = aggregationServiceFactory.getAggregationService(); ApplicationScope applicationScope =CpNamingUtils.getApplicationScope(applicationId); return aggregationService.getApplicationSize(applicationScope); } @Override public Map<String,Long> getEachCollectionSize(final UUID applicationId) { AggregationService aggregationService = aggregationServiceFactory.getAggregationService(); ApplicationScope applicationScope =CpNamingUtils.getApplicationScope(applicationId); return aggregationService.getEachCollectionSize(applicationScope); } @Override public long getCollectionSize(final UUID applicationId, final String collectionName) { AggregationService aggregationService = aggregationServiceFactory.getAggregationService(); ApplicationScope applicationScope =CpNamingUtils.getApplicationScope(applicationId); return aggregationService.getSize(applicationScope, CpNamingUtils.createCollectionSearchEdge(applicationScope.getApplication(), collectionName)); } protected Entity getOrganizationEntityForApplication( UUID applicationInfoId ) throws Exception { if ( applicationInfoId == null ) { return null; } final EntityManager em = emf.getEntityManager(smf.getManagementAppId()); Results r = em.getSourceEntities( new SimpleEntityRef(CpNamingUtils.APPLICATION_INFO, applicationInfoId), ORG_APP_RELATIONSHIP, Group.ENTITY_TYPE, Level.ALL_PROPERTIES); return r.getEntity(); } @Override public UUID getOrganizationIdForApplication( UUID applicationInfoId ) throws Exception { Entity entity = getOrganizationEntityForApplication(applicationInfoId); return entity != null ? entity.getUuid() : null; } @Override public OrganizationInfo getOrganizationForApplication( UUID applicationInfoId ) throws Exception { Entity entity = getOrganizationEntityForApplication(applicationInfoId); return entity != null ? new OrganizationInfo( entity.getUuid(), ( String ) entity.getProperty( "path" ) ) : null; } @Override public BiMap<UUID, String> getApplicationsForOrganization( UUID organizationGroupId ) throws Exception { if ( organizationGroupId == null ) { return null; } final BiMap<UUID, String> applications = HashBiMap.create(); final EntityManager em = emf.getEntityManager(smf.getManagementAppId()); // query for application_info entities final Results results = em.getTargetEntities( new SimpleEntityRef(Group.ENTITY_TYPE, organizationGroupId), ORG_APP_RELATIONSHIP, CpNamingUtils.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(); } // make sure we return applicationId and not the application_info UUID UUID applicationId = entity.getUuid(); applications.put( applicationId, 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; } /** * @return UUID of the application itself (NOT the application_info entity). */ @Override public UUID addApplicationToOrganization(UUID organizationId, Entity appInfo) throws Exception { UUID applicationId = appInfo.getUuid(); if ( ( organizationId == null ) || ( applicationId == null ) ) { return null; } EntityManager em = emf.getEntityManager(smf.getManagementAppId()); em.createConnection( new SimpleEntityRef( Group.ENTITY_TYPE, organizationId ), ORG_APP_RELATIONSHIP, appInfo ); 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( smf.getManagementAppId() ); Entity entity = em.get(new SimpleEntityRef(CpNamingUtils.APPLICATION_INFO, applicationId)); if ( entity != null ) { return new ApplicationInfo( applicationId, entity.getName() ); } return null; } @Override public ApplicationInfo getDeletedApplicationInfo(UUID applicationId) throws Exception { if ( applicationId == null ) { return null; } EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); final Entity entity = em.get( new SimpleEntityRef( CpNamingUtils.DELETED_APPLICATION_INFO, 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 entityId ) throws Exception { if ( AuthPrincipalType.ORGANIZATION.equals( type )) { UUID ownerId = smf.getManagementAppId(); return getCredentialsSecret( readUserToken( ownerId, entityId, Group.ENTITY_TYPE ) ); } else if ( AuthPrincipalType.APPLICATION.equals( type ) ) { UUID ownerId = smf.getManagementAppId(); return getCredentialsSecret( readUserToken( ownerId, entityId, Application.ENTITY_TYPE ) ); } else if ( AuthPrincipalType.ADMIN_USER.equals( type ) || AuthPrincipalType.APPLICATION_USER.equals( type ) ) { return getCredentialsSecret( readUserPasswordCredentials( applicationId, entityId, User.ENTITY_TYPE ) ); } 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(smf.getManagementAppId(), 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(smf.getManagementAppId(), AuthPrincipalType.APPLICATION, applicationId); } public String newSecretKey( AuthPrincipalType type, UUID id ) throws Exception { String secret = generateOAuthSecretKey( type ); writeUserToken( smf.getManagementAppId(), new SimpleEntityRef( type.getEntityType(), id ), encryptionService.plainTextCredentials( secret, id, smf.getManagementAppId() ) ); 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( smf.getManagementAppId(), type, uuid ) ) ) { String token = getTokenForPrincipal( ACCESS, null, smf.getManagementAppId(), 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( smf.getManagementAppId(), 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, UUID organizationId ) throws Exception { return getTokenForPrincipal( EMAIL, TOKEN_TYPE_PASSWORD_RESET, smf.getManagementAppId(), ADMIN_USER, userId, ttl, organizationId ); } @Override public TokenInfo getPasswordResetTokenInfoForAdminUser( String token ) throws Exception { TokenInfo tokenInfo = null; try { tokenInfo = getTokenInfoFromAccessToken(token, TOKEN_TYPE_PASSWORD_RESET, ADMIN_USER); } catch (Exception e) { // intentionally empty } return tokenInfo; } @Override public boolean checkPasswordResetTokenForAdminUser( UUID userId, String token ) throws Exception { return checkPasswordResetTokenForAdminUser(userId, getPasswordResetTokenInfoForAdminUser(token)); } @Override public boolean checkPasswordResetTokenForAdminUser( UUID userId, TokenInfo tokenInfo ) throws Exception { if (tokenInfo == null) { return false; } AuthPrincipalInfo principal = null; try { principal = tokenInfo.getPrincipal(); } 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, UUID organizationId ) throws Exception { return getTokenForPrincipal( EMAIL, TOKEN_TYPE_ACTIVATION, smf.getManagementAppId(), ADMIN_USER, userId, ttl, organizationId ); } @Override public String getConfirmationTokenForAdminUser( UUID userId, long ttl, UUID organizationId ) throws Exception { return getTokenForPrincipal( EMAIL, TOKEN_TYPE_CONFIRM, smf.getManagementAppId(), ADMIN_USER, userId, ttl, organizationId ); } @Override public void activateAdminUser( UUID userId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( User.ENTITY_TYPE, userId ), "activated", true ); invalidateManagementAppAuthCache(); } @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( smf.getManagementAppId() ); 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( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( User.ENTITY_TYPE, userId ), "confirmed", true ); } @Override public void unconfirmAdminUser( UUID userId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( User.ENTITY_TYPE, userId ), "confirmed", false ); } @Override public boolean isAdminUserConfirmed( UUID userId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( User.ENTITY_TYPE, userId ), "disabled", false ); } @Override public void disableAdminUser( UUID userId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( User.ENTITY_TYPE, userId ), "disabled", true ); revokeAccessTokensForAdminUser(userId); } @Override public boolean isAdminUserEnabled( UUID userId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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( UUID organizationId, UserInfo user ) throws Exception { String token = getPasswordResetTokenForAdminUser( user.getUuid(), 0, organizationId ); OrganizationConfig orgConfig = organizationId != null ? getOrganizationConfigByUuid(organizationId) : getOrganizationConfigForUserInfo(user); String resetPropertyUrl = orgConfig.getFullUrlTemplate(WorkflowUrl.ADMIN_RESETPW_URL); String reset_url = String.format(resetPropertyUrl, user.getUuid().toString()) + "?token=" + token; Map<String, String> pageContext = hashMap( "reset_url", reset_url ) .map( "reset_url_base", resetPropertyUrl ) .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, smf.getManagementAppId(), ORGANIZATION, organizationId, ttl ); } @Override public void startOrganizationActivationFlow( OrganizationInfo organization ) throws Exception { logger.info( "startOrganizationActivationFlow: {}", organization.getName() ); try { UUID organizationId = organization.getUuid(); String token = getActivationTokenForOrganization( organizationId, 0 ); OrganizationConfig orgConfig = getOrganizationConfigByUuid(organizationId); String activationPropertyUrl = orgConfig.getFullUrlTemplate(WorkflowUrl.ORGANIZATION_ACTIVATION_URL); String activation_url = String.format(activationPropertyUrl, organizationId.toString()) + "?token=" + token; List<UserInfo> users = getAdminUsersForOrganization( organizationId ); 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() ); //TODO: org shouldn't approve org activation, right? sendHtmlMail( properties, getOrgSystemEmail(), 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(); } //TODO: email for org admin or sysadmin? sendHtmlMail( properties, getOrgSystemEmail(), 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(UUID organizationId, UserInfo user, boolean fromReactivate) throws Exception { // re-activation flow ( or auto-activated flow ) if ( user.isActivated() ) { // send email always if from re-activation, otherwise check the property flag if ( fromReactivate || newAdminUsersRequireConfirmation() ) { sendAdminUserConfirmationEmail(organizationId, user); } sendAdminUserActivatedEmail( user ); sendSysAdminNewAdminActivatedNotificationEmail( organizationId, user ); } else { if ( newAdminUsersRequireConfirmation() ) { sendAdminUserConfirmationEmail( organizationId, user ); } else if ( newAdminUsersNeedSysAdminApproval() ) { sendSysAdminRequestAdminActivationEmail( organizationId, 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 TokenInfo getConfirmationTokenInfoForAdminUser( String token ) throws Exception { return getTokenInfoFromAccessToken(token, TOKEN_TYPE_CONFIRM, ADMIN_USER); } @Override public ActivationState handleConfirmationTokenForAdminUser( UUID userId, String token ) throws Exception { return handleConfirmationTokenForAdminUser(userId, getConfirmationTokenInfoForAdminUser(token)); } @Override // token may contain the workflow organization id public ActivationState handleConfirmationTokenForAdminUser( UUID userId, TokenInfo tokenInfo ) throws Exception { if (tokenInfo != null) { AuthPrincipalInfo principal = tokenInfo.getPrincipal(); if ((principal != null) && userId.equals(principal.getUuid())) { UUID workflowOrgId = tokenInfo.getWorkflowOrgId(); UserInfo user = getAdminUserByUuid(principal.getUuid()); confirmAdminUser(user.getUuid()); if (newAdminUsersNeedSysAdminApproval()) { sendAdminUserConfirmedAwaitingActivationEmail(user); sendSysAdminRequestAdminActivationEmail(workflowOrgId, user); return ActivationState.CONFIRMED_AWAITING_ACTIVATION; } else { activateAdminUser(principal.getUuid()); sendAdminUserActivatedEmail(user); sendSysAdminNewAdminActivatedNotificationEmail(workflowOrgId, user); return ActivationState.ACTIVATED; } } } return ActivationState.UNKNOWN; } @Override public TokenInfo getActivationTokenInfoForAdminUser( String token ) throws Exception { return getTokenInfoFromAccessToken(token, TOKEN_TYPE_ACTIVATION, ADMIN_USER); } @Override public ActivationState handleActivationTokenForAdminUser( UUID userId, String token ) throws Exception { return handleActivationTokenForAdminUser(userId, getActivationTokenInfoForAdminUser(token)); } @Override // token may contain the workflow organization id public ActivationState handleActivationTokenForAdminUser( UUID userId, TokenInfo tokenInfo ) throws Exception { if (tokenInfo != null) { AuthPrincipalInfo principal = tokenInfo.getPrincipal(); if ((principal != null) && userId.equals(principal.getUuid())) { UUID workflowOrgId = tokenInfo.getWorkflowOrgId(); activateAdminUser(principal.getUuid()); UserInfo user = getAdminUserByUuid(principal.getUuid()); sendAdminUserActivatedEmail(user); sendSysAdminNewAdminActivatedNotificationEmail(workflowOrgId, user); return ActivationState.ACTIVATED; } } return ActivationState.UNKNOWN; } public void sendAdminUserConfirmationEmail( UUID organizationId, UserInfo user ) throws Exception { String token = getConfirmationTokenForAdminUser(user.getUuid(), 0, organizationId); OrganizationConfig orgConfig = organizationId != null ? getOrganizationConfigByUuid(organizationId) : getOrganizationConfigForUserInfo(user); String confirmation_url = orgConfig.getFullUrl(WorkflowUrl.ADMIN_CONFIRMATION_URL, user.getUuid().toString()) + "?token=" + token; sendAdminUserEmail(user, "User Account Confirmation: " + user.getEmail(), emailMsg(hashMap("confirm_email", user.getEmail()).map("confirmation_url", confirmation_url), PROPERTIES_EMAIL_ADMIN_CONFIRMATION)); } public void sendSysAdminRequestAdminActivationEmail( UUID organizationId, UserInfo user ) throws Exception { String token = getActivationTokenForAdminUser(user.getUuid(), 0, organizationId); //TODO: admin specific email OrganizationConfig orgConfig = organizationId != null ? getOrganizationConfigByUuid(organizationId) : getOrganizationConfigForUserInfo(user); String activation_url = orgConfig.getFullUrl(WorkflowUrl.ADMIN_ACTIVATION_URL, user.getUuid().toString()) + "?token=" + token; String adminSystemEmail = organizationId != null ? getAdminSystemEmailForOrganization(organizationId) : getAdminSystemEmailForApplication(user.getApplicationId()); sendHtmlMail(properties, adminSystemEmail, 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( UUID organizationId, UserInfo user ) throws Exception { if ( properties.notifySysAdminOfNewAdminUsers() ) { String adminSystemEmail = organizationId != null ? getAdminSystemEmailForOrganization(organizationId) : getAdminSystemEmailForApplication(user.getApplicationId()); sendHtmlMail( properties, adminSystemEmail, 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( smf.getManagementAppId() ); 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( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( Group.ENTITY_TYPE, organizationId ), "activated", false ); } @Override public boolean isOrganizationActivated( UUID organizationId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( Group.ENTITY_TYPE, organizationId ), "disabled", false ); } @Override public void disableOrganization( UUID organizationId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); em.setProperty( new SimpleEntityRef( Group.ENTITY_TYPE, organizationId ), "disabled", true ); } @Override public boolean isOrganizationEnabled( UUID organizationId ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); 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()); OrganizationConfig orgConfig = getOrganizationConfigForApplication(applicationId); String resetPropertyUrl = orgConfig.getFullUrlTemplate(WorkflowUrl.USER_RESETPW_URL); String reset_url = buildUserAppUrl( applicationId, resetPropertyUrl, user, token); Map<String, String> pageContext = hashMap( "reset_url", reset_url ) .map( "reset_url_base", resetPropertyUrl ) .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; } @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; } 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; } @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()); OrganizationConfig orgConfig = getOrganizationConfigForApplication(applicationId); String confirmationPropertyUrl = orgConfig.getFullUrlTemplate(WorkflowUrl.USER_CONFIRMATION_URL); String confirmation_url = buildUserAppUrl( applicationId, confirmationPropertyUrl, 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()); OrganizationInfo organization = this.getOrganizationForApplication( applicationId ); OrganizationConfig orgConfig = getOrganizationConfigByUuid(organization.getUuid()); String activationPropertyUrl = orgConfig.getFullUrlTemplate(WorkflowUrl.USER_ACTIVATION_URL); String activation_url = buildUserAppUrl(applicationId, activationPropertyUrl, user, token); /* * String activation_url = String.format( * properties.getProperty(PROPERTIES_USER_ACTIVATION_URL), * applicationId.toString(), user.getUuid().toString()) + "?token=" + token; */ 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; } Collection<String> policyVioliations = passwordPolicy.policyCheck( newPassword, false ); if ( !policyVioliations.isEmpty() ) { throw new PasswordPolicyViolationException( passwordPolicy.getDescription( false ), policyVioliations ); } 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 void setAppUserCredentialsInfo( final UUID applicationId, final UUID userId, final CredentialsInfo credentialsInfo ) throws Exception { Preconditions.checkNotNull( applicationId, "applicationId is required" ); Preconditions.checkNotNull( userId, "userId is required" ); Preconditions.checkNotNull( credentialsInfo, "credentialsInfo is required" ); final User user = emf.getEntityManager( applicationId ).get(userId, User.class); if(user == null){ throw new EntityNotFoundException( "User with id " + userId + " cannot be found" ); } writeUserPassword(applicationId, user, credentialsInfo); } @Override public CredentialsInfo getAppUserCredentialsInfo( final UUID applicationId, final UUID userId ) throws Exception { final User user = emf.getEntityManager( applicationId ).get( userId, User.class ); if(user == null){ throw new EntityNotFoundException("Could not find user with id " + userId + " in application" + applicationId ); } final CredentialsInfo ci = readUserPasswordCredentials( applicationId, userId, User.ENTITY_TYPE ); if ( ci == null ) { throw new EntityNotFoundException("Could not find credentials for user with id " + userId + " in application" + applicationId ); } return ci; } @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, user.getType() ) ); 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(), user.getType() ) ) ) ) { return user; } return null; } @Override public void countAdminUserAction( UserInfo user, String action ) throws Exception { EntityManager em = emf.getEntityManager(smf.getManagementAppId()); 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( smf.getManagementAppId() ); Group org = em.get( orgId, Group.class ); if ( org == null ) { throw new EntityNotFoundException( String.format( "Could not find organization with id %s", orgId.toString() ) ); } 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( smf.getManagementAppId() ); 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, String ownerType ) throws Exception { return readCreds( appId, ownerId, ownerType, 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, String ownerType ) throws Exception { return readCreds( appId, ownerId, ownerType, 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, String ownerType ) throws Exception { return readCreds( appId, ownerId, ownerType, 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, String ownerType ) throws Exception { return readCreds( appId, ownerId, ownerType, 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 ownerType, 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( new SimpleEntityRef("user", ownerId )); @SuppressWarnings("unchecked") Set<CredentialsInfo> credInfo = (Set<CredentialsInfo>)em.getDictionaryElementValue(owner, DICTIONARY_CREDENTIALS, USER_PASSWORD_HISTORY); return credInfo; } @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, User.ENTITY_TYPE ); return (ci != null) && 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; } @Override public Observable<Id> deleteAllEntities(final UUID applicationId,final int limit){ if(applicationId.equals(CpNamingUtils.MANAGEMENT_APPLICATION_ID)){ throw new IllegalArgumentException("Can't delete from management app"); } return service.deleteAllEntities(CpNamingUtils.getApplicationScope(applicationId),limit); } private String getProperty(String key) { String obj = properties.getProperty(key); if(StringUtils.isEmpty(obj)) return null; else return obj; } private boolean getBooleanProperty(String key) { String obj = getProperty(key); return !StringUtils.isEmpty(obj) && Boolean.parseBoolean(obj); } @Override public OrganizationConfig getOrganizationConfigDefaultsOnly() { return new OrganizationConfig(orgConfigProperties); } @Override public OrganizationConfig getOrganizationConfigByName( String organizationName ) throws Exception { if ( organizationName == null || organizationName.equals(CassandraService.MANAGEMENT_APPLICATION)) { return getOrganizationConfigDefaultsOnly(); } EntityManager em = emf.getEntityManager(smf.getManagementAppId()); EntityRef ref = em.getAlias( Group.ENTITY_TYPE, organizationName ); return ref != null ? getOrganizationConfigByUuid(ref.getUuid()) : getOrganizationConfigDefaultsOnly(); } @Override public OrganizationConfig getOrganizationConfigByUuid( UUID id ) throws Exception { EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); Entity entity = em.get( new SimpleEntityRef( Group.ENTITY_TYPE, id ) ); if ( entity == null ) { return getOrganizationConfigDefaultsOnly(); } Map<Object, Object> entityProperties = em.getDictionaryAsMap(entity, ORGANIZATION_CONFIG_DICTIONARY); return new OrganizationConfig( orgConfigProperties, (UUID)entity.getProperty(PROPERTY_UUID), (String)entity.getProperty(PROPERTY_PATH), entityProperties, false); } private OrganizationConfig getOrganizationConfigForUserInfo(UserInfo user) throws Exception { UUID userApp = user.getApplicationId(); if (userApp == CpNamingUtils.MANAGEMENT_APPLICATION_ID) { Map<UUID, String> organizations = getOrganizationsForAdminUser(user.getUuid()); if (organizations != null) { Iterator<UUID> iter = organizations.keySet().iterator(); if (iter.hasNext()) { return getOrganizationConfigByUuid(iter.next()); } } } else { // if user is not an admin, use associated application return getOrganizationConfigForApplication(userApp); } // return default return getOrganizationConfigDefaultsOnly(); } private String getOrganizationConfigPropertyForUserInfo(UserInfo user, String key) throws Exception { return getOrganizationConfigForUserInfo(user).getProperty(key); } private String getOrganizationConfigPropertyForApplication(UUID applicationId, String key) throws Exception { return getOrganizationConfigForApplication(applicationId).getProperty(key); } private String getOrganizationConfigPropertyByUuid(UUID organizationId, String key) throws Exception { return getOrganizationConfigByUuid(organizationId).getProperty(key); } @Override public OrganizationConfig getOrganizationConfigForApplication( UUID applicationInfoId ) throws Exception { // return from the cache. if the orgconfig cannot be loaded, defaults are loaded and cached return orgConfigByAppCache.get( applicationInfoId ); } @Override public void updateOrganizationConfig( OrganizationConfig organizationConfig ) throws Exception { // get only the overrides Map<String, Object> orgConfigProperties = organizationConfig.getOrgConfigOverridesMap(); if ( orgConfigProperties != null ) { EntityRef organizationEntity = new SimpleEntityRef( Group.ENTITY_TYPE, organizationConfig.getUuid() ); EntityManager em = emf.getEntityManager( smf.getManagementAppId() ); for ( Map.Entry<String, Object> entry : orgConfigProperties.entrySet() ) { if ( "".equals( entry.getValue() ) ) { em.removeFromDictionary( organizationEntity, ORGANIZATION_CONFIG_DICTIONARY, entry.getKey() ); } else { em.addToDictionary( organizationEntity, ORGANIZATION_CONFIG_DICTIONARY, entry.getKey(), entry.getValue() ); } } // evict cache for this key if it exists orgConfigByAppCache.invalidate( organizationConfig.getUuid() ); } } private void invalidateManagementAppAuthCache() { ScopedCache scopedCache = cacheFactory.getScopedCache( new CacheScope( new SimpleId( CpNamingUtils.MANAGEMENT_APPLICATION_ID, "application" ))); scopedCache.invalidate(); localShiroCache.invalidateAll(); } @Override public void createOrganizationPostProcessing( final OrganizationInfo orgInfo, final Map<String,String> properties ){ // do nothing, this is a hook for any classes extending the ManagementServiceInterface } @Override public void createAdminUserPostProcessing( final UserInfo userInfo, final Map<String,String> properties){ // do nothing, this is a hook for any classes extending the ManagementServiceInterface } @Override public void addUserToOrganizationPostProcessing( final UserInfo userInfo, final String organizationName, final Map<String,String> properties){ // do nothing, this is a hook for any classes extending the ManagementServiceInterface } @Override public void removeUserFromOrganizationPostProcessing( final UserInfo userInfo, final String organizationName, final Map<String,String> properties){ // do nothing, this is a hook for any classes extending the ManagementServiceInterface } }