adding a separate class for UsergridCentral (implements ExternalTokenProvider). Cleaning up tokenServiceImpl -> first check if its user grid token else if SSOProvider is enabled then validate the external token.
Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/c60aaa75 Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/c60aaa75 Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/c60aaa75 Branch: refs/heads/apigee-sso-provider Commit: c60aaa751e57b380d8c489f7e91a63094da7f41b Parents: c23f16a Author: Ayesha Dastagiri <[email protected]> Authored: Thu Jun 23 17:11:51 2016 -0700 Committer: Ayesha Dastagiri <[email protected]> Committed: Thu Jun 23 17:11:51 2016 -0700 ---------------------------------------------------------------------- .../main/resources/usergrid-default.properties | 28 +- .../rest/management/ManagementResource.java | 7 +- .../organizations/OrganizationsResource.java | 6 +- .../rest/management/users/UserResource.java | 25 +- .../rest/management/users/UsersResource.java | 7 +- .../rest/management/ManagementResourceIT.java | 10 +- .../tokens/cassandra/TokenServiceImpl.java | 246 ++---------------- .../externalProviders/ApigeeSSO2Provider.java | 54 ++-- .../ExternalTokenProvider.java | 6 +- .../externalProviders/UsergridCentral.java | 259 +++++++++++++++++++ .../resources/usergrid-services-context.xml | 4 + 11 files changed, 365 insertions(+), 287 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/config/src/main/resources/usergrid-default.properties ---------------------------------------------------------------------- diff --git a/stack/config/src/main/resources/usergrid-default.properties b/stack/config/src/main/resources/usergrid-default.properties index 2758bfb..2783e0e 100644 --- a/stack/config/src/main/resources/usergrid-default.properties +++ b/stack/config/src/main/resources/usergrid-default.properties @@ -470,18 +470,19 @@ usergrid.push.queuemanager.cache.size=200 -############################### Usergrid Central SSO ############################# -# -# Usergrid has a feature to provide a distributing SSO system. The below configurations -# allow you to configure the central Usergrid SSO server. -# - -# Set the base URL of the central Usergrid SSO server. This will enable -# External Token Validation for Admin Users and will configure this Usergrid -# instance to delegate all Admin User authentication to the central Usergrid SSO -# server. See also: https://issues.apache.org/jira/browse/USERGRID-567 -# -usergrid.central.url= +################################ Usergrid Central SSO ############################# +## +## Usergrid has a feature to provide a distributing SSO system. The below configurations +## allow you to configure the central Usergrid SSO server. +## +# +## Set the base URL of the central Usergrid SSO server. This will enable +## External Token Validation for Admin Users and will configure this Usergrid +## instance to delegate all Admin User authentication to the central Usergrid SSO +## server. See also: https://issues.apache.org/jira/browse/USERGRID-567 +## +#usergrid.central.url= +#usergrid.central.enabled= # Set the HTTP Client connection pool for connections to the SSO central server. # @@ -492,8 +493,9 @@ usergrid.central.read.timeout=10000 ############################### Usergrid SSO2 ############################# -usergrid.external.sso.enabled=true +usergrid.external.sso.enabled= usergrid.external.sso.provider=apigee +usergrid.external.sso.url= usergrid.external.sso.publicKeyUrl=https://login.apigee.com/token_key ############################### Usergrid Assets ############################# http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java index c4a921c..13fedd2 100644 --- a/stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java +++ b/stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java @@ -53,7 +53,8 @@ import java.util.Map; import static javax.servlet.http.HttpServletResponse.*; import static javax.ws.rs.core.MediaType.*; import static org.apache.commons.lang.StringUtils.isNotBlank; -import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_CENTRAL_URL; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_SSO_ENABLED; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER_URL; import static org.apache.usergrid.utils.JsonUtils.mapToJsonString; import static org.apache.usergrid.utils.StringUtils.stringOrSubstringAfterFirst; import static org.apache.usergrid.utils.StringUtils.stringOrSubstringBeforeFirst; @@ -483,7 +484,7 @@ public class ManagementResource extends AbstractContextResource { } final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + !StringUtils.isEmpty( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { @@ -494,7 +495,7 @@ public class ManagementResource extends AbstractContextResource { // this guy is not the superuser throw new IllegalArgumentException( "Admin Users must login via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } } } http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/OrganizationsResource.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/OrganizationsResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/OrganizationsResource.java index 360e660..0e77d97 100644 --- a/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/OrganizationsResource.java +++ b/stack/rest/src/main/java/org/apache/usergrid/rest/management/organizations/OrganizationsResource.java @@ -40,7 +40,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import java.util.*; -import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_CENTRAL_URL; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER_URL; @Component( "org.apache.usergrid.rest.management.organizations.OrganizationsResource" ) @@ -186,11 +186,11 @@ public class OrganizationsResource extends AbstractContextResource { Map<String, Object> orgProperties, String callback ) throws Exception { final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + !StringUtils.isEmpty( properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Organization / Admin Users must be created via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } Preconditions http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UserResource.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UserResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UserResource.java index 28d46a6..e431579 100644 --- a/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UserResource.java +++ b/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UserResource.java @@ -20,13 +20,11 @@ package org.apache.usergrid.rest.management.users; import com.fasterxml.jackson.jaxrs.json.annotation.JSONP; import net.tanesha.recaptcha.ReCaptchaImpl; import net.tanesha.recaptcha.ReCaptchaResponse; -import org.apache.commons.lang.StringUtils; import org.apache.usergrid.management.ActivationState; import org.apache.usergrid.management.UserInfo; import org.apache.usergrid.rest.AbstractContextResource; import org.apache.usergrid.rest.ApiResponse; import org.apache.usergrid.rest.exceptions.RedirectionException; -import org.apache.usergrid.rest.management.ManagementResource; import org.apache.usergrid.rest.management.users.organizations.OrganizationsResource; import org.apache.usergrid.rest.security.annotations.RequireAdminUserAccess; import org.apache.usergrid.security.tokens.TokenInfo; @@ -46,8 +44,9 @@ import java.util.Map; import java.util.UUID; import static org.apache.usergrid.security.shiro.utils.SubjectUtils.isServiceAdmin; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER_URL; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_SSO_ENABLED; import static org.apache.usergrid.utils.ConversionUtils.string; -import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_CENTRAL_URL; @Component( "org.apache.usergrid.rest.management.users.UserResource" ) @@ -212,11 +211,11 @@ public class UserResource extends AbstractContextResource { public Viewable showPasswordResetForm( @Context UriInfo ui, @QueryParam( "token" ) String token ) { final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + Boolean.valueOf( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Admin Users must reset passwords via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } UUID organizationId = null; @@ -259,11 +258,11 @@ public class UserResource extends AbstractContextResource { } final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + Boolean.valueOf( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Admin Users must reset passwords via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } UUID organizationId = null; @@ -348,11 +347,11 @@ public class UserResource extends AbstractContextResource { public Viewable activate( @Context UriInfo ui, @QueryParam( "token" ) String token ) { final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + Boolean.valueOf( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Admin Users must activate via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } UUID organizationId = null; @@ -381,11 +380,11 @@ public class UserResource extends AbstractContextResource { public Viewable confirm( @Context UriInfo ui, @QueryParam( "token" ) String token ) { final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + Boolean.valueOf( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Admin Users must confirm via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } UUID organizationId = null; @@ -420,11 +419,11 @@ public class UserResource extends AbstractContextResource { throws Exception { final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + Boolean.valueOf( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Admin Users must reactivate via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } logger.info( "Send activation email for user: {}" , user.getUuid() ); http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UsersResource.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UsersResource.java b/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UsersResource.java index ff279ef..7356124 100644 --- a/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UsersResource.java +++ b/stack/rest/src/main/java/org/apache/usergrid/rest/management/users/UsersResource.java @@ -44,7 +44,8 @@ import java.util.UUID; import static org.apache.commons.lang.StringUtils.isBlank; import static org.apache.usergrid.rest.exceptions.SecurityException.mappableSecurityException; -import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_CENTRAL_URL; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_SSO_ENABLED; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER_URL; @Component( "org.apache.usergrid.rest.management.users.UsersResource" ) @@ -115,11 +116,11 @@ public class UsersResource extends AbstractContextResource { throws Exception { final boolean externalTokensEnabled = - !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) ); + Boolean.valueOf( properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED ) ); if ( externalTokensEnabled ) { throw new IllegalArgumentException( "Admin Users must signup via " + - properties.getProperty( USERGRID_CENTRAL_URL ) ); + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); } // email is only required parameter http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/rest/src/test/java/org/apache/usergrid/rest/management/ManagementResourceIT.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/management/ManagementResourceIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/management/ManagementResourceIT.java index 6bf9117..1fe4f01 100644 --- a/stack/rest/src/test/java/org/apache/usergrid/rest/management/ManagementResourceIT.java +++ b/stack/rest/src/test/java/org/apache/usergrid/rest/management/ManagementResourceIT.java @@ -37,7 +37,7 @@ import javax.ws.rs.core.Response; import java.io.IOException; import java.util.*; -import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_CENTRAL_URL; +import static org.apache.usergrid.security.tokens.cassandra.TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER_URL; import static org.apache.usergrid.utils.MapUtils.hashMap; import static org.junit.Assert.*; @@ -632,7 +632,7 @@ public class ManagementResourceIT extends AbstractRestIT { String suToken = clientSetup.getSuperuserToken().getAccessToken(); Map<String, String> props = new HashMap<String, String>(); - props.put( USERGRID_CENTRAL_URL, getBaseURI().toURL().toExternalForm() ); + props.put( USERGRID_EXTERNAL_PROVIDER_URL, getBaseURI().toURL().toExternalForm() ); pathResource( "testproperties" ).post( props ); @@ -652,7 +652,7 @@ public class ManagementResourceIT extends AbstractRestIT { // unset the Usergrid Central SSO URL so it does not interfere with other tests - props.put( USERGRID_CENTRAL_URL, "" ); + props.put( USERGRID_EXTERNAL_PROVIDER_URL, "" ); pathResource( "testproperties" ).post( props ); } @@ -671,7 +671,7 @@ public class ManagementResourceIT extends AbstractRestIT { String suToken = clientSetup.getSuperuserToken().getAccessToken(); Map<String, String> props = new HashMap<String, String>(); - props.put( USERGRID_CENTRAL_URL, getBaseURI().toURL().toExternalForm() ); + props.put( USERGRID_EXTERNAL_PROVIDER_URL, getBaseURI().toURL().toExternalForm() ); pathResource( "testproperties" ).post( props ); try { @@ -713,7 +713,7 @@ public class ManagementResourceIT extends AbstractRestIT { // turn off validate external tokens by un-setting the usergrid.central.url - props.put( USERGRID_CENTRAL_URL, "" ); + props.put( USERGRID_EXTERNAL_PROVIDER_URL, "" ); pathResource( "testproperties" ).post( props ); } } http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java ---------------------------------------------------------------------- diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java index c07bb15..eef7a1c 100644 --- a/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java +++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/cassandra/TokenServiceImpl.java @@ -17,23 +17,19 @@ package org.apache.usergrid.security.tokens.cassandra; -import com.codahale.metrics.Counter; import com.google.inject.Injector; import me.prettyprint.hector.api.Keyspace; import me.prettyprint.hector.api.beans.HColumn; import me.prettyprint.hector.api.mutation.Mutator; -import org.apache.commons.lang.RandomStringUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.usergrid.corepersistence.CpEntityManagerFactory; import org.apache.usergrid.corepersistence.util.CpNamingUtils; -import org.apache.usergrid.exception.NotImplementedException; -import org.apache.usergrid.management.*; +import org.apache.usergrid.management.ApplicationCreator; +import org.apache.usergrid.management.ManagementService; +import org.apache.usergrid.management.UserInfo; import org.apache.usergrid.persistence.EntityManagerFactory; import org.apache.usergrid.persistence.cassandra.CassandraService; import org.apache.usergrid.persistence.core.metrics.MetricsFactory; import org.apache.usergrid.persistence.entities.Application; -import org.apache.usergrid.persistence.exceptions.EntityNotFoundException; import org.apache.usergrid.security.AuthPrincipalInfo; import org.apache.usergrid.security.AuthPrincipalType; import org.apache.usergrid.security.tokens.TokenCategory; @@ -43,15 +39,10 @@ import org.apache.usergrid.security.tokens.exceptions.BadTokenException; import org.apache.usergrid.security.tokens.exceptions.ExpiredTokenException; import org.apache.usergrid.security.tokens.exceptions.InvalidTokenException; import org.apache.usergrid.security.tokens.externalProviders.ApigeeSSO2Provider; +import org.apache.usergrid.security.tokens.externalProviders.UsergridCentral; import org.apache.usergrid.utils.ConversionUtils; import org.apache.usergrid.utils.JsonUtils; import org.apache.usergrid.utils.UUIDUtils; -import org.codehaus.jackson.JsonNode; -import org.glassfish.jersey.apache.connector.ApacheClientProperties; -import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; -import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.jackson.JacksonFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -59,8 +50,6 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.util.Assert; import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.core.MediaType; import java.nio.ByteBuffer; import java.util.*; @@ -327,27 +316,28 @@ public class TokenServiceImpl implements TokenService { @Override public TokenInfo getTokenInfo( String token, boolean updateAccessTime ) throws Exception { - if(isApigeeSSO2Enabled()) { - //TODO: pass the correct token ttl. - return validateExternalToken(token,maxPersistenceTokenAge ,properties.getProperty(USERGRID_EXTERNAL_PROVIDER)); + UUID uuid = null; + try{ + uuid = getUUIDForToken( token ); } - - - UUID uuid = getUUIDForToken( token ); - - long ssoTtl = 1000000L; // TODO: property for this - - if ( uuid == null ) { - return isSSOEnabled() ? validateExternalToken( token, ssoTtl, "" ) : null; + catch(Exception e){ + try{ + return validateExternalToken(token,1,properties.getProperty(USERGRID_EXTERNAL_PROVIDER)); + } + catch (Exception exception){ + logger.debug("invalid request"); + throw new IllegalArgumentException("Invalid token in the request."); + } } + long ssoTtl = 1000000L; // TODO: property for this TokenInfo tokenInfo; try { tokenInfo = getTokenInfo( uuid ); } catch (InvalidTokenException e){ // now try from central sso - if ( isSSOEnabled() ){ + if ( isExternalSSOProviderEnabled() ){ return validateExternalToken( token, maxPersistenceTokenAge,"" ); }else{ throw e; // re-throw the error @@ -751,7 +741,6 @@ public class TokenServiceImpl implements TokenService { // // Central SSO implementation - public static final String USERGRID_CENTRAL_URL = "usergrid.central.url"; public static final String CENTRAL_CONNECTION_POOL_SIZE = "usergrid.central.connection.pool.size"; public static final String CENTRAL_CONNECTION_TIMEOUT = "usergrid.central.connection.timeout"; public static final String CENTRAL_READ_TIMEOUT = "usergrid.central.read.timeout"; @@ -766,6 +755,7 @@ public class TokenServiceImpl implements TokenService { //SSO2 implementation public static final String USERGRID_EXTERNAL_SSO_ENABLED = "usergrid.external.sso.enabled"; public static final String USERGRID_EXTERNAL_PROVIDER = "usergrid.external.sso.provider"; + public static final String USERGRID_EXTERNAL_PROVIDER_URL = "usergrid.external.sso.url"; @@ -781,11 +771,8 @@ public class TokenServiceImpl implements TokenService { return metricsFactory; } - private boolean isSSOEnabled() { - return !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL )); - } - private boolean isApigeeSSO2Enabled() { + private boolean isExternalSSOProviderEnabled() { return Boolean.valueOf(properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED )); } @@ -807,205 +794,24 @@ public class TokenServiceImpl implements TokenService { * @param ttl Time to live for token. */ public TokenInfo validateExternalToken(String extAccessToken, long ttl, String provider) throws Exception { - TokenInfo tokenInfo = null; - //todo: based on provider call the appropriate method. if(provider.equalsIgnoreCase("apigee")){ ApigeeSSO2Provider apigeeProvider = ((CpEntityManagerFactory)emf).getApplicationContext().getBean( ApigeeSSO2Provider.class ); - return apigeeProvider.validateAndReturnUserInfo(extAccessToken); - } - - if (!isSSOEnabled()) { - throw new NotImplementedException( "External Token Validation Service not enabled" ); - } - - if (extAccessToken == null) { - throw new IllegalArgumentException( "ext_access_token must be specified" ); + return apigeeProvider.validateAndReturnTokenInfo(extAccessToken,ttl); } - - if (ttl == -1) { - throw new IllegalArgumentException( "ttl must be specified" ); - } - - com.codahale.metrics.Timer processingTimer = getMetricsFactory().getTimer( - TokenServiceImpl.class, SSO_PROCESSING_TIME ); - - com.codahale.metrics.Timer.Context timerContext = processingTimer.time(); - - try { - // look up user via UG Central's /management/me endpoint. - - JsonNode accessInfoNode = getMeFromUgCentral( extAccessToken ); - - JsonNode userNode = accessInfoNode.get( "user" ); - - String username = userNode.get( "username" ).asText(); - - // if user does not exist locally then we need to fix that - - UserInfo userInfo = management.getAdminUserByUsername( username ); - UUID userId = userInfo == null ? null : userInfo.getUuid(); - - if (userId == null) { - - // create local user and and organizations they have on the central Usergrid instance - logger.info( "User {} does not exist locally, creating", username ); - - String name = userNode.get( "name" ).asText(); - String email = userNode.get( "email" ).asText(); - String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 ); - - JsonNode orgsNode = userNode.get( "organizations" ); - Iterator<String> fieldNames = orgsNode.getFieldNames(); - - if (!fieldNames.hasNext()) { - // no organizations for user exist in response from central Usergrid SSO - // so create user's personal organization and use username as organization name - fieldNames = Collections.singletonList( username ).iterator(); - } - - // create user and any organizations that user is supposed to have - - while (fieldNames.hasNext()) { - - String orgName = fieldNames.next(); - - if (userId == null) { - - // haven't created user yet so do that now - OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization( - orgName, username, name, email, dummyPassword, true, false ); - - applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() ); - - userId = ownerOrgInfo.getOwner().getUuid(); - userInfo = ownerOrgInfo.getOwner(); - - Counter createdAdminsCounter = getMetricsFactory().getCounter( - TokenServiceImpl.class, SSO_CREATED_LOCAL_ADMINS ); - createdAdminsCounter.inc(); - - logger.info( "Created user {} and org {}", username, orgName ); - - } else { - - // already created user, so just create an org - final OrganizationInfo organization = - management.createOrganization( orgName, userInfo, true ); - - applicationCreator.createSampleFor( organization ); - - logger.info( "Created user {}'s other org {}", username, orgName ); - } - } - } + else if(provider.equalsIgnoreCase("usergridCentral")){ + UsergridCentral ugCentralProvider = ((CpEntityManagerFactory)emf).getApplicationContext().getBean( UsergridCentral.class ); + UserInfo userinfo = ugCentralProvider.validateAndReturnUserInfo(extAccessToken,ttl); // store the external access_token as if it were one of our own importToken( extAccessToken, TokenCategory.ACCESS, null, new AuthPrincipalInfo( - ADMIN_USER, userId, CpNamingUtils.MANAGEMENT_APPLICATION_ID), null, ttl ); - - tokenInfo = getTokenInfo( extAccessToken ); + ADMIN_USER, userinfo.getUuid(), CpNamingUtils.MANAGEMENT_APPLICATION_ID), null, ttl ); + return getTokenInfo( extAccessToken ); - } catch (Exception e) { - timerContext.stop(); - logger.debug( "Error validating external token", e ); - throw e; } - + //todo : what if the token info is null ? return tokenInfo; } - /** - * Look up Admin User via UG Central's /management/me endpoint. - * - * @param extAccessToken Access token issued by UG Central of Admin User - * @return JsonNode representation of AccessInfo object for Admin User - * @throws EntityNotFoundException if access_token is not valid. - */ - private JsonNode getMeFromUgCentral( String extAccessToken ) throws EntityNotFoundException { - - // prepare to count tokens validated and rejected - - Counter tokensRejectedCounter = getMetricsFactory().getCounter( - TokenServiceImpl.class, SSO_TOKENS_REJECTED ); - Counter tokensValidatedCounter = getMetricsFactory().getCounter( - TokenServiceImpl.class, SSO_TOKENS_VALIDATED ); - - // create URL of central Usergrid's /management/me endpoint - - String externalUrl = properties.getProperty( USERGRID_CENTRAL_URL ).trim(); - - // be lenient about trailing slash - externalUrl = !externalUrl.endsWith( "/" ) ? externalUrl + "/" : externalUrl; - String me = externalUrl + "management/me?access_token=" + extAccessToken; - - // use our favorite HTTP client to GET /management/me - - Client client = getJerseyClient(); - final JsonNode accessInfoNode; - try { - accessInfoNode = client.target( me ).request() - .accept( MediaType.APPLICATION_JSON_TYPE ) - .get(JsonNode.class); - - tokensValidatedCounter.inc(); - - } catch ( Exception e ) { - // user not found 404 - tokensRejectedCounter.inc(); - String msg = "Cannot find Admin User associated with " + extAccessToken; - throw new EntityNotFoundException( msg, e ); - } - - return accessInfoNode; - } - - - - private Client getJerseyClient() { - - if ( jerseyClient == null ) { - - synchronized ( this ) { - - // create HTTPClient and with configured connection pool - - int poolSize = 100; // connections - final String poolSizeStr = properties.getProperty( CENTRAL_CONNECTION_POOL_SIZE ); - if ( poolSizeStr != null ) { - poolSize = Integer.parseInt( poolSizeStr ); - } - - PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(); - connectionManager.setMaxTotal(poolSize); - - int timeout = 20000; // ms - final String timeoutStr = properties.getProperty( CENTRAL_CONNECTION_TIMEOUT ); - if ( timeoutStr != null ) { - timeout = Integer.parseInt( timeoutStr ); - } - - int readTimeout = 20000; // ms - final String readTimeoutStr = properties.getProperty( CENTRAL_READ_TIMEOUT ); - if ( readTimeoutStr != null ) { - readTimeout = Integer.parseInt( readTimeoutStr ); - } - - ClientConfig clientConfig = new ClientConfig(); - clientConfig.register( new JacksonFeature() ); - clientConfig.property( ApacheClientProperties.CONNECTION_MANAGER, connectionManager ); - clientConfig.connectorProvider( new ApacheConnectorProvider() ); - - jerseyClient = ClientBuilder.newClient( clientConfig ); - jerseyClient.property( ClientProperties.CONNECT_TIMEOUT, timeout ); - jerseyClient.property( ClientProperties.READ_TIMEOUT, readTimeout ); - } - } - - return jerseyClient; - - } - - } http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ApigeeSSO2Provider.java ---------------------------------------------------------------------- diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ApigeeSSO2Provider.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ApigeeSSO2Provider.java index 028d244..08ec781 100644 --- a/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ApigeeSSO2Provider.java +++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ApigeeSSO2Provider.java @@ -34,13 +34,9 @@ public class ApigeeSSO2Provider implements ExternalTokenProvider { private static final Logger logger = LoggerFactory.getLogger(ApigeeSSO2Provider.class); private static final String RESPONSE_PUBLICKEY_VALUE = "value"; - protected Properties properties; - protected ManagementService management; - protected Client client; - protected String publicKey; public static final String USERGRID_EXTERNAL_PUBLICKEY_URL = "usergrid.external.sso.publicKeyUrl"; @@ -52,54 +48,60 @@ public class ApigeeSSO2Provider implements ExternalTokenProvider { } private String getPublicKey() { - return client.target(properties.getProperty(USERGRID_EXTERNAL_PUBLICKEY_URL)).request().get(Map.class). - get(RESPONSE_PUBLICKEY_VALUE).toString(); + Map<String, Object> publicKey = client.target(properties.getProperty(USERGRID_EXTERNAL_PUBLICKEY_URL)).request().get(Map.class); + return publicKey.get(RESPONSE_PUBLICKEY_VALUE).toString().split("----\n")[1].split("\n---")[0]; } @Override - public TokenInfo validateAndReturnUserInfo(String token) throws Exception { + public TokenInfo validateAndReturnTokenInfo(String token, long ttl) throws Exception { + try { + UserInfo userInfo = validateAndReturnUserInfo(token, ttl); + TokenInfo tokeninfo = new TokenInfo(UUIDUtils.newTimeUUID(), "access", 1, 1, 1, ttl, + new AuthPrincipalInfo(AuthPrincipalType.ADMIN_USER, userInfo.getUuid(), + CpNamingUtils.MANAGEMENT_APPLICATION_ID), null); + return tokeninfo; + } + catch(Exception e){ + logger.debug("Error construcing token info from userinfo"); + e.printStackTrace(); + } + return null; + } + + @Override + public UserInfo validateAndReturnUserInfo(String token, long ttl) throws Exception { - //todo:// check cache , if its invalid retrieve from the USERGRID_EXTERNAL_PUBLICKEY_URL - String key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0w+/0m5ENuLEAUxPxTMm\nXrJ8bhkyPunC2VCLTaA+2dkYhR0pfPM4RtCO9tqkjc63Raos3OTph9I9gE7RMBiU\n0GVMBDYe74OLZjpGeI7OO8TmQhaOeLzX/ej9QBvq1gbhHt1QGP3m43/g1bN64Ggt\nBq4NCXm1Ie80NvdxWDsKifhYi4fgo+zhcMBaSE2Hhyc3TIg1oEKfh+EmHL/4LhPd\n7CYl4PxqR+DVNbJrdGeGOteWX4p5sW79t/8CsnvJ4St5Yv3sGK5JuBbmGiKW8wWE\nn+9bDg5i3SPlimMBdySH+wMbFULyfVSvJHeMmEAMHKDq+PVGdM+znNkPCCVIkHZG\nRQIDAQAB"; - byte[] publicBytes = decodeBase64(key); + byte[] publicBytes = decodeBase64(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey pubKey = keyFactory.generatePublic(keySpec); Jws<Claims> payload = null; try { payload = Jwts.parser().setSigningKey(pubKey).parseClaimsJws(token); - System.out.println(payload.getBody()); - //todo : construct the jsonNode object - or should we construct the Tokeninfo?? UserInfo userInfo = management.getAdminUserByEmail(payload.getBody().get("email").toString()); - TokenInfo tokeninfo = new TokenInfo(UUIDUtils.newTimeUUID(), "access", 1, 1, 1, 1, - new AuthPrincipalInfo(AuthPrincipalType.ADMIN_USER, userInfo.getUuid(), - CpNamingUtils.MANAGEMENT_APPLICATION_ID), null); - return tokeninfo; - } catch (SignatureException se) { - //if this exception is thrown, first check if the token is expired. Else retirve the public key from the EXTERNAL_URL - try{ - getPublicKey(); - } - catch(Exception e){ - + if (userInfo == null) { + throw new IllegalArgumentException("user " + payload.getBody().get("email").toString() + " doesnt exist"); } - logger.debug("signature did not match"); + return userInfo; + } catch (SignatureException se) { + logger.debug("Signature did not match."); + throw new IllegalArgumentException("Signature did not match for the token."); } catch (Exception e) { + logger.debug("Error validating Apigee SSO2 token."); e.printStackTrace(); } return null; } - @Autowired public void setManagement(ManagementService management) { this.management = management; } @Autowired - public void setProperties( Properties properties ) { + public void setProperties(Properties properties) { this.properties = properties; this.publicKey = getPublicKey(); } http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ExternalTokenProvider.java ---------------------------------------------------------------------- diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ExternalTokenProvider.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ExternalTokenProvider.java index cd00cdf..935c8ad 100644 --- a/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ExternalTokenProvider.java +++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ExternalTokenProvider.java @@ -1,5 +1,6 @@ package org.apache.usergrid.security.tokens.externalProviders; +import org.apache.usergrid.management.UserInfo; import org.apache.usergrid.security.tokens.TokenInfo; /** @@ -8,6 +9,9 @@ import org.apache.usergrid.security.tokens.TokenInfo; public interface ExternalTokenProvider { /** Authenticate a userId and external token against this provider */ - TokenInfo validateAndReturnUserInfo(String token) throws Exception; + TokenInfo validateAndReturnTokenInfo(String token, long ttl) throws Exception; + + /** Authenticate a userId and external token against this provider */ + UserInfo validateAndReturnUserInfo(String token, long ttl) throws Exception; } http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/UsergridCentral.java ---------------------------------------------------------------------- diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/UsergridCentral.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/UsergridCentral.java new file mode 100644 index 0000000..fd07d6f --- /dev/null +++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/UsergridCentral.java @@ -0,0 +1,259 @@ +package org.apache.usergrid.security.tokens.externalProviders; + +import com.codahale.metrics.Counter; +import com.google.inject.Injector; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.usergrid.management.*; +import org.apache.usergrid.persistence.core.metrics.MetricsFactory; +import org.apache.usergrid.persistence.exceptions.EntityNotFoundException; +import org.apache.usergrid.security.tokens.TokenInfo; +import org.codehaus.jackson.JsonNode; +import org.glassfish.jersey.apache.connector.ApacheClientProperties; +import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.MediaType; +import java.util.Collections; +import java.util.Iterator; +import java.util.Properties; +import java.util.UUID; + +/** + * Created by ayeshadastagiri on 6/23/16. + */ +public class UsergridCentral implements ExternalTokenProvider { + private static final Logger logger = LoggerFactory.getLogger(ApigeeSSO2Provider.class); + + private static final String SSO_PROCESSING_TIME = "sso.processing_time"; + private static final String SSO_TOKENS_REJECTED = "sso.tokens_rejected"; + private static final String SSO_TOKENS_VALIDATED = "sso.tokens_validated"; + public static final String USERGRID_CENTRAL_URL = "usergrid.external.sso.publicKeyUrl"; + public static final String CENTRAL_CONNECTION_POOL_SIZE = "usergrid.central.connection.pool.size"; + public static final String CENTRAL_CONNECTION_TIMEOUT = "usergrid.central.connection.timeout"; + public static final String CENTRAL_READ_TIMEOUT = "usergrid.central.read.timeout"; + private static final String SSO_CREATED_LOCAL_ADMINS = "sso.created_local_admins"; + + protected ManagementService management; + protected MetricsFactory metricsFactory; + protected Properties properties; + + private static Client jerseyClient = null; + + @Autowired + private Injector injector; + + @Autowired + private ApplicationCreator applicationCreator; + + @Autowired + public void setManagement(ManagementService management) { + this.management = management; + } + + @Autowired + public void setProperties(Properties properties) { + this.properties = properties; + } + + @Autowired + public void setMetricFactory() { + this.metricsFactory = injector.getInstance(MetricsFactory.class); + } + + MetricsFactory getMetricsFactory() { + return metricsFactory; + } + + @Override + public TokenInfo validateAndReturnTokenInfo(String token, long ttl) throws Exception { + return null; + } + + @Override + public UserInfo validateAndReturnUserInfo(String token, long ttl) throws Exception { + if (token == null) { + throw new IllegalArgumentException("ext_access_token must be specified"); + } + if (ttl == -1) { + throw new IllegalArgumentException("ttl must be specified"); + } + + com.codahale.metrics.Timer processingTimer = getMetricsFactory().getTimer( + UsergridCentral.class, SSO_PROCESSING_TIME); + + com.codahale.metrics.Timer.Context timerContext = processingTimer.time(); + + try { + // look up user via UG Central's /management/me endpoint. + + JsonNode accessInfoNode = getMeFromUgCentral(token); + + JsonNode userNode = accessInfoNode.get("user"); + + String username = userNode.get("username").asText(); + + // if user does not exist locally then we need to fix that + + UserInfo userInfo = management.getAdminUserByUsername(username); + UUID userId = userInfo == null ? null : userInfo.getUuid(); + + if (userId == null) { + + // create local user and and organizations they have on the central Usergrid instance + logger.info("User {} does not exist locally, creating", username); + + String name = userNode.get("name").asText(); + String email = userNode.get("email").asText(); + String dummyPassword = RandomStringUtils.randomAlphanumeric(40); + + JsonNode orgsNode = userNode.get("organizations"); + Iterator<String> fieldNames = orgsNode.getFieldNames(); + + if (!fieldNames.hasNext()) { + // no organizations for user exist in response from central Usergrid SSO + // so create user's personal organization and use username as organization name + fieldNames = Collections.singletonList(username).iterator(); + } + + // create user and any organizations that user is supposed to have + + while (fieldNames.hasNext()) { + + String orgName = fieldNames.next(); + + if (userId == null) { +// + // haven't created user yet so do that now + OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization( + orgName, username, name, email, dummyPassword, true, false); + + applicationCreator.createSampleFor(ownerOrgInfo.getOrganization()); + + userId = ownerOrgInfo.getOwner().getUuid(); + userInfo = ownerOrgInfo.getOwner(); + + Counter createdAdminsCounter = getMetricsFactory().getCounter( + UsergridCentral.class, SSO_CREATED_LOCAL_ADMINS); + createdAdminsCounter.inc(); + + logger.info("Created user {} and org {}", username, orgName); + + } else { + + // already created user, so just create an org + final OrganizationInfo organization = + management.createOrganization(orgName, userInfo, true); + + applicationCreator.createSampleFor(organization); + + logger.info("Created user {}'s other org {}", username, orgName); + } + } + } + + return userInfo; + } catch (Exception e) { + timerContext.stop(); + logger.debug("Error validating external token", e); + throw e; + } + + } + + /** + * Look up Admin User via UG Central's /management/me endpoint. + * + * @param extAccessToken Access token issued by UG Central of Admin User + * @return JsonNode representation of AccessInfo object for Admin User + * @throws EntityNotFoundException if access_token is not valid. + */ + private JsonNode getMeFromUgCentral(String extAccessToken) throws EntityNotFoundException { + + // prepare to count tokens validated and rejected + + Counter tokensRejectedCounter = getMetricsFactory().getCounter( + UsergridCentral.class, SSO_TOKENS_REJECTED); + Counter tokensValidatedCounter = getMetricsFactory().getCounter( + UsergridCentral.class, SSO_TOKENS_VALIDATED); + + // create URL of central Usergrid's /management/me endpoint + + String externalUrl = properties.getProperty(USERGRID_CENTRAL_URL).trim(); + + // be lenient about trailing slash + externalUrl = !externalUrl.endsWith("/") ? externalUrl + "/" : externalUrl; + String me = externalUrl + "management/me?access_token=" + extAccessToken; + + // use our favorite HTTP client to GET /management/me + + Client client = getJerseyClient(); + final org.codehaus.jackson.JsonNode accessInfoNode; + try { + accessInfoNode = client.target(me).request() + .accept(MediaType.APPLICATION_JSON_TYPE) + .get(org.codehaus.jackson.JsonNode.class); + + tokensValidatedCounter.inc(); + + } catch (Exception e) { + // user not found 404 + tokensRejectedCounter.inc(); + String msg = "Cannot find Admin User associated with " + extAccessToken; + throw new EntityNotFoundException(msg, e); + } + + return accessInfoNode; + } + + private Client getJerseyClient() { + + if (jerseyClient == null) { + + synchronized (this) { + + // create HTTPClient and with configured connection pool + + int poolSize = 100; // connections + final String poolSizeStr = properties.getProperty(CENTRAL_CONNECTION_POOL_SIZE); + if (poolSizeStr != null) { + poolSize = Integer.parseInt(poolSizeStr); + } + + PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(); + connectionManager.setMaxTotal(poolSize); + + int timeout = 20000; // ms + final String timeoutStr = properties.getProperty(CENTRAL_CONNECTION_TIMEOUT); + if (timeoutStr != null) { + timeout = Integer.parseInt(timeoutStr); + } + + int readTimeout = 20000; // ms + final String readTimeoutStr = properties.getProperty(CENTRAL_READ_TIMEOUT); + if (readTimeoutStr != null) { + readTimeout = Integer.parseInt(readTimeoutStr); + } + + ClientConfig clientConfig = new ClientConfig(); + clientConfig.register(new JacksonFeature()); + clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager); + clientConfig.connectorProvider(new ApacheConnectorProvider()); + + jerseyClient = ClientBuilder.newClient(clientConfig); + jerseyClient.property(ClientProperties.CONNECT_TIMEOUT, timeout); + jerseyClient.property(ClientProperties.READ_TIMEOUT, readTimeout); + } + } + + return jerseyClient; + + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/c60aaa75/stack/services/src/main/resources/usergrid-services-context.xml ---------------------------------------------------------------------- diff --git a/stack/services/src/main/resources/usergrid-services-context.xml b/stack/services/src/main/resources/usergrid-services-context.xml index 29a4b7c..c80139c 100644 --- a/stack/services/src/main/resources/usergrid-services-context.xml +++ b/stack/services/src/main/resources/usergrid-services-context.xml @@ -82,6 +82,10 @@ <property name="management" ref="managementService" /> </bean> + <bean id="usergridCentral" class="org.apache.usergrid.security.tokens.externalProviders.UsergridCentral"> + <property name="management" ref="managementService" /> + </bean> + <bean id="serviceManagerFactory" class="org.apache.usergrid.services.ServiceManagerFactory"> <constructor-arg ref="entityManagerFactory"/> <constructor-arg ref="properties"/>
