blob: 746d9dd9e040e71bd2d0599a49cb99ebef0ef278 [file] [log] [blame]
/*
* 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.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.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;
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);
}
@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 ){
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 = createAdminUserInternal(null, username, name, email, password, true, false, userProperties);
} else {
user = createAdminUserInternal(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() ) {
// TODO T.N. temporary hack to deal with duplicate orgs. Revert this
// commit after migration
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);
}
}
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 {
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.info( "createAdminUserInternal: {}", username );
if ( isBlank( password ) ) {
password = encodeBase64URLSafeString( bytes( UUID.randomUUID() ) );
}
if ( username == null ) {
username = email;
}
if ( name == null ) {
name = email;
}
EntityManager em = emf.getEntityManager( 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;
}
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 );
}
@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 deep ) 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 ( deep ) {
BiMap<UUID, String> applications = getApplicationsForOrganization( organization.getKey() );
jsonOrganization.put( "applications", applications.inverse() );
List<UserInfo> users = getAdminUsersForOrganization( organization.getKey() );
Map<String, Object> jsonUsers = new HashMap<>();
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 ) {
if(!tokens.isExternalSSOProviderEnabled()) {
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 ) throws Exception {
if ( user.isActivated() ) {
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;
}
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();
}
}