Repository: usergrid Updated Branches: refs/heads/apigee-sso-provider ead9e89c2 -> b21e47780
initial commit for ApogeeSSO2-Usergrid integration. Added an interface for ExternalToken providers and ApigeeSSO2Provider class which validates the jwt tokens. Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/c23f16a3 Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/c23f16a3 Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/c23f16a3 Branch: refs/heads/apigee-sso-provider Commit: c23f16a350d6c653c61884406a3fabbfcf06254a Parents: f212608 Author: Ayesha Dastagiri <[email protected]> Authored: Thu Jun 23 03:54:13 2016 -0700 Committer: Ayesha Dastagiri <[email protected]> Committed: Thu Jun 23 03:54:13 2016 -0700 ---------------------------------------------------------------------- .../main/resources/usergrid-default.properties | 4 + stack/services/pom.xml | 7 ++ .../tokens/cassandra/TokenServiceImpl.java | 31 +++++- .../externalProviders/ApigeeSSO2Provider.java | 106 +++++++++++++++++++ .../ExternalTokenProvider.java | 13 +++ .../resources/usergrid-services-context.xml | 3 + 6 files changed, 160 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid/blob/c23f16a3/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 d2141cf..2758bfb 100644 --- a/stack/config/src/main/resources/usergrid-default.properties +++ b/stack/config/src/main/resources/usergrid-default.properties @@ -491,6 +491,10 @@ usergrid.central.read.timeout=10000 +############################### Usergrid SSO2 ############################# +usergrid.external.sso.enabled=true +usergrid.external.sso.provider=apigee +usergrid.external.sso.publicKeyUrl=https://login.apigee.com/token_key ############################### Usergrid Assets ############################# # http://git-wip-us.apache.org/repos/asf/usergrid/blob/c23f16a3/stack/services/pom.xml ---------------------------------------------------------------------- diff --git a/stack/services/pom.xml b/stack/services/pom.xml index 06ae5d8..fdbd9c9 100644 --- a/stack/services/pom.xml +++ b/stack/services/pom.xml @@ -175,6 +175,13 @@ <artifactId>shiro-core</artifactId> </dependency> + <!-- http://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + <version>0.2</version> + </dependency> + <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> http://git-wip-us.apache.org/repos/asf/usergrid/blob/c23f16a3/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 5c71b1b..c07bb15 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 @@ -42,6 +42,7 @@ import org.apache.usergrid.security.tokens.TokenService; 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.utils.ConversionUtils; import org.apache.usergrid.utils.JsonUtils; import org.apache.usergrid.utils.UUIDUtils; @@ -326,21 +327,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 = getUUIDForToken( token ); long ssoTtl = 1000000L; // TODO: property for this if ( uuid == null ) { - return isSSOEnabled() ? validateExternalToken( token, ssoTtl ) : null; + return isSSOEnabled() ? validateExternalToken( token, ssoTtl, "" ) : null; } + TokenInfo tokenInfo; try { tokenInfo = getTokenInfo( uuid ); } catch (InvalidTokenException e){ // now try from central sso if ( isSSOEnabled() ){ - return validateExternalToken( token, maxPersistenceTokenAge ); + return validateExternalToken( token, maxPersistenceTokenAge,"" ); }else{ throw e; // re-throw the error } @@ -748,12 +756,19 @@ public class TokenServiceImpl implements TokenService { public static final String CENTRAL_CONNECTION_TIMEOUT = "usergrid.central.connection.timeout"; public static final String CENTRAL_READ_TIMEOUT = "usergrid.central.read.timeout"; + // names for metrics to be collected private static final String SSO_TOKENS_REJECTED = "sso.tokens_rejected"; private static final String SSO_TOKENS_VALIDATED = "sso.tokens_validated"; private static final String SSO_CREATED_LOCAL_ADMINS = "sso.created_local_admins"; private static final String SSO_PROCESSING_TIME = "sso.processing_time"; + //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"; + + + private static Client jerseyClient = null; @Autowired @@ -770,6 +785,9 @@ public class TokenServiceImpl implements TokenService { return !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL )); } + private boolean isApigeeSSO2Enabled() { + return Boolean.valueOf(properties.getProperty( USERGRID_EXTERNAL_SSO_ENABLED )); + } /** * <p> @@ -788,10 +806,16 @@ public class TokenServiceImpl implements TokenService { * @param extAccessToken Access token from external Usergrid system. * @param ttl Time to live for token. */ - public TokenInfo validateExternalToken(String extAccessToken, long ttl) throws Exception { + 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" ); } @@ -892,7 +916,6 @@ public class TokenServiceImpl implements TokenService { return tokenInfo; } - /** * Look up Admin User via UG Central's /management/me endpoint. * http://git-wip-us.apache.org/repos/asf/usergrid/blob/c23f16a3/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 new file mode 100644 index 0000000..028d244 --- /dev/null +++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ApigeeSSO2Provider.java @@ -0,0 +1,106 @@ +package org.apache.usergrid.security.tokens.externalProviders; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureException; +import org.apache.usergrid.corepersistence.util.CpNamingUtils; +import org.apache.usergrid.management.ManagementService; +import org.apache.usergrid.management.UserInfo; +import org.apache.usergrid.security.AuthPrincipalInfo; +import org.apache.usergrid.security.AuthPrincipalType; +import org.apache.usergrid.security.tokens.TokenInfo; +import org.apache.usergrid.utils.UUIDUtils; +import org.glassfish.jersey.client.ClientConfig; +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 java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; +import java.util.Map; +import java.util.Properties; + +import static org.apache.commons.codec.binary.Base64.decodeBase64; + +/** + * Created by ayeshadastagiri on 6/22/16. + */ +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"; + + public ApigeeSSO2Provider() { + ClientConfig clientConfig = new ClientConfig(); + clientConfig.register(new JacksonFeature()); + client = ClientBuilder.newClient(clientConfig); + } + + private String getPublicKey() { + return client.target(properties.getProperty(USERGRID_EXTERNAL_PUBLICKEY_URL)).request().get(Map.class). + get(RESPONSE_PUBLICKEY_VALUE).toString(); + } + + @Override + public TokenInfo validateAndReturnUserInfo(String token) 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); + 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){ + + } + logger.debug("signature did not match"); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + + @Autowired + public void setManagement(ManagementService management) { + this.management = management; + } + + @Autowired + public void setProperties( Properties properties ) { + this.properties = properties; + this.publicKey = getPublicKey(); + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/c23f16a3/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 new file mode 100644 index 0000000..cd00cdf --- /dev/null +++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/externalProviders/ExternalTokenProvider.java @@ -0,0 +1,13 @@ +package org.apache.usergrid.security.tokens.externalProviders; + +import org.apache.usergrid.security.tokens.TokenInfo; + +/** + * Created by ayeshadastagiri on 6/22/16. + */ +public interface ExternalTokenProvider { + + /** Authenticate a userId and external token against this provider */ + TokenInfo validateAndReturnUserInfo(String token) throws Exception; + +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/c23f16a3/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 c064f60..29a4b7c 100644 --- a/stack/services/src/main/resources/usergrid-services-context.xml +++ b/stack/services/src/main/resources/usergrid-services-context.xml @@ -78,6 +78,9 @@ <bean id="saltProvider" class="org.apache.usergrid.security.salt.NoOpSaltProvider"/> + <bean id="apigeeSSO2Provider" class="org.apache.usergrid.security.tokens.externalProviders.ApigeeSSO2Provider"> + <property name="management" ref="managementService" /> + </bean> <bean id="serviceManagerFactory" class="org.apache.usergrid.services.ServiceManagerFactory"> <constructor-arg ref="entityManagerFactory"/>
