Cleanup. Add tests.
Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/6d88bcfd Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/6d88bcfd Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/6d88bcfd Branch: refs/heads/apigee-sso-provider Commit: 6d88bcfdccc458b382b16483b5d3048df699262c Parents: 5fcad44 Author: Ayesha Dastagiri <ayesha.am...@gmail.com> Authored: Tue Jul 26 11:49:42 2016 -0700 Committer: Ayesha Dastagiri <ayesha.am...@gmail.com> Committed: Tue Jul 26 11:49:42 2016 -0700 ---------------------------------------------------------------------- stack/rest/pom.xml | 8 + .../rest/management/ManagementResource.java | 13 +- .../rest/management/ExternalSSOEnabledIT.java | 175 +++++++++++++++++++ .../rest/management/ManagementResourceIT.java | 24 ++- .../security/sso/ApigeeSSO2Provider.java | 28 +-- .../tokens/cassandra/TokenServiceImpl.java | 3 + 6 files changed, 233 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid/blob/6d88bcfd/stack/rest/pom.xml ---------------------------------------------------------------------- diff --git a/stack/rest/pom.xml b/stack/rest/pom.xml index 89d14b4..580814c 100644 --- a/stack/rest/pom.xml +++ b/stack/rest/pom.xml @@ -306,6 +306,14 @@ <version>${jackson-2-version}</version> </dependency> + <!-- for mocking the Apigee SSO Service --> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + <!-- databinding; ObjectMapper, JsonNode and related classes are here --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> http://git-wip-us.apache.org/repos/asf/usergrid/blob/6d88bcfd/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 7217c49..c5d7c77 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 @@ -385,9 +385,14 @@ public class ManagementResource extends AbstractContextResource { //moved the check for sso enabled form MangementServiceImpl since was unable to get the current user there to check if its super user. if( tokens.isExternalSSOProviderEnabled() - && !userServiceAdmin(user.getUsername()) ){ - throw new RuntimeException("SSO Integration is enabled, Admin users must login via provider: "+ - properties.getProperty(TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER)); + && !userServiceAdmin(username) ){ + OAuthResponse response = + OAuthResponse.errorResponse( SC_BAD_REQUEST ).setError( OAuthError.TokenResponse.INVALID_GRANT ) + .setErrorDescription( "SSO Integration is enabled, Admin users must login via provider: "+ + properties.getProperty(TokenServiceImpl.USERGRID_EXTERNAL_PROVIDER) ).buildJSONMessage(); + return Response.status( response.getResponseStatus() ).type( jsonMediaType( callback ) ) + .entity( wrapWithCallback( response.getBody(), callback ) ).build(); + } String token = management.getAccessTokenForAdminUser( user.getUuid(), ttl ); @@ -616,7 +621,7 @@ public class ManagementResource extends AbstractContextResource { if ( tokens.isExternalSSOProviderEnabled() ) { // when external tokens enabled then only superuser can obtain an access token - if ( userServiceAdmin(username)) { + if ( !userServiceAdmin(username)) { // this guy is not the superuser throw new IllegalArgumentException( "Admin Users must login via " + properties.getProperty( USERGRID_EXTERNAL_PROVIDER_URL ) ); http://git-wip-us.apache.org/repos/asf/usergrid/blob/6d88bcfd/stack/rest/src/test/java/org/apache/usergrid/rest/management/ExternalSSOEnabledIT.java ---------------------------------------------------------------------- diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/management/ExternalSSOEnabledIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/management/ExternalSSOEnabledIT.java new file mode 100644 index 0000000..75fa0b5 --- /dev/null +++ b/stack/rest/src/test/java/org/apache/usergrid/rest/management/ExternalSSOEnabledIT.java @@ -0,0 +1,175 @@ +/* + * 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.rest.management; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.impl.crypto.RsaProvider; +import org.apache.commons.collections4.map.HashedMap; +import org.apache.usergrid.cassandra.SpringResource; +import org.apache.usergrid.persistence.index.utils.UUIDUtils; +import org.apache.usergrid.rest.test.resource.AbstractRestIT; +import org.apache.usergrid.rest.test.resource.RestClient; +import org.apache.usergrid.rest.test.resource.model.ApiResponse; +import org.apache.usergrid.rest.test.resource.model.Entity; +import org.apache.usergrid.security.sso.ApigeeSSO2Provider; +import org.codehaus.jackson.JsonNode; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.security.*; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Created by ayeshadastagiri on 7/20/16. + */ +@Ignore("Need to figure out a way to set the public key for Mock server.") +public class ExternalSSOEnabledIT extends AbstractRestIT { + + Key key; + PublicKey publicKey; + PrivateKey privateKey; + String compactJws; + String username = "SSOadminuser" + UUIDUtils.newTimeUUID(); + ApigeeSSO2Provider apigeeSSO2ProviderTest; + //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 ExternalSSOEnabledIT() throws Exception { + + } + + @Before + public void setup() throws NoSuchAlgorithmException { + generateKey(); + } + + private void generateKey() { + KeyPair kp = RsaProvider.generateKeyPair(1024); + publicKey = kp.getPublic(); + privateKey = kp.getPrivate(); + } + + private String genrateToken(){ + Map<String, Object> claims = new HashedMap<String, Object>(); + claims.put("jti","c7df0339-3847-450b-a925-628ef237953a"); + claims.put("sub","b6d62259-217b-4e96-8f49-e00c366e4fed"); + claims.put("scope","size = 5"); + claims.put("client_id", "edgecli"); + claims.put("azp","edgecli"); + claims.put("grant_type" ,"password"); + claims.put("user_id","b6d62259-217b-4e96-8f49-e00c366e4fed"); + claims.put( "origin","usergrid"); + claims.put("user_name","AyeshaSSOUser"); + claims.put("email", "adastagiri+ssotest...@apigee.com"); + claims.put( "rev_sig","dfe5d0d3"); + claims.put("iat","1466550862"); + claims.put("exp", System.currentTimeMillis() + 1000); + claims.put("iss", "https://login.apigee.com/oauth/token"); + claims.put( "zid","uaa"); + claims.put( "aud"," size = 6"); + claims.put("grant_type","password"); + + String jwt = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS256, privateKey).compact(); + return jwt; + + } + + @Test + public void SuperUserTestsFor() throws NoSuchAlgorithmException { + + // create a admin user. + RestClient restClient = clientSetup.getRestClient(); + + //Create adminUser values + Entity adminUserPayload = new Entity(); + adminUserPayload.put("username", "TestUser"); + adminUserPayload.put("name", username); + adminUserPayload.put("email", "adastagiri+ssotest...@apigee.com"); + adminUserPayload.put("password", username); + + //create adminUser + ApiResponse adminUserEntityResponse = management().orgs().org(clientSetup.getOrganizationName()).users().post(ApiResponse.class, adminUserPayload); + + Entity adminUserResponse = new Entity(adminUserEntityResponse); + //verify that the response contains the correct data + assertNotNull(adminUserResponse); + assertEquals("TestUser", adminUserResponse.get("username")); + + Map<String, String> props = new HashMap<String, String>(); + + props.put( USERGRID_EXTERNAL_SSO_ENABLED, "true" ); + props.put( USERGRID_EXTERNAL_PROVIDER, "apigee" ); + pathResource( "testproperties" ).post( props ); + + // /management/me --> superuser and query params --> Generate a super usesr token. + Map<String, Object> loginInfo = new HashMap<String, Object>() {{ + put( "username", "superuser" ); + put( "password", "superpassword" ); + put( "grant_type", "password" ); + }}; + ApiResponse postResponse2 = pathResource( "management/token" ).post( false,ApiResponse.class,loginInfo ); + assertTrue(postResponse2.getAccessToken() != null ); + + + // /orgs create an org with superuser credentials. + // /management/me --> superuser and query params --> Generate a super usesr token. + Map<String, Object> orgDetails = new HashMap<String, Object>() {{ + put( "email", "adastagiri+ssotest...@apigee.com" ); + put( "name", "testuser" ); + put( "organization", username ); + }}; + + context().getToken().put("access_token",postResponse2.getAccessToken()); + postResponse2 = pathResource( "management/orgs" ).post( true,ApiResponse.class,orgDetails); + assertTrue(postResponse2.getData() != null); + + postResponse2 = pathResource("management/orgs").get(ApiResponse.class,true); + assertTrue(postResponse2 != null); + + + compactJws = genrateToken(); + + SpringResource.getInstance().getAppContext().getBean(ApigeeSSO2Provider.class).setPublicKey( publicKey ); + context().getToken().put("access_token",compactJws); + // /management/me --> admin user and jwt token. Return the user information and "token" should have jwt token. + JsonNode responseToken = management().me().get(JsonNode.class,true); + assertTrue(responseToken.get("access_token") != null); + + + // /management/me --> admin and query params --> Generate a super usesr token. + Map<String, Object> loginInfo1 = new HashMap<String, Object>() {{ + put( "username", "TestUser" ); + put( "password", username ); + put( "grant_type", "password" ); + }}; + + // /managment/token -> adminusername and password --> should fail. + ApiResponse postResponse1 = pathResource("management/token").post(false, ApiResponse.class,loginInfo1); +// fail( "SSO Integration is enabled, Admin users must login via provider: "+ USERGRID_EXTERNAL_PROVIDER_URL); + + + + + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/6d88bcfd/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 1fe4f01..21c62ba 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 @@ -38,6 +38,7 @@ import java.io.IOException; import java.util.*; 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.MapUtils.hashMap; import static org.junit.Assert.*; @@ -671,6 +672,7 @@ public class ManagementResourceIT extends AbstractRestIT { String suToken = clientSetup.getSuperuserToken().getAccessToken(); Map<String, String> props = new HashMap<String, String>(); + props.put(USERGRID_EXTERNAL_SSO_ENABLED, "true"); props.put( USERGRID_EXTERNAL_PROVIDER_URL, getBaseURI().toURL().toExternalForm() ); pathResource( "testproperties" ).post( props ); @@ -685,7 +687,7 @@ public class ManagementResourceIT extends AbstractRestIT { put( "grant_type", "password" ); }}; ApiResponse postResponse = pathResource( "management/token" ).post( false, ApiResponse.class, loginInfo ); - fail( "Login as Admin User must fail when validate external tokens is enabled" ); + fail( "SSO Integration is enabled, Admin users must login via provider: "+ USERGRID_EXTERNAL_PROVIDER_URL); } catch (ClientErrorException actual) { assertEquals( 400, actual.getResponse().getStatus() ); @@ -709,11 +711,31 @@ public class ManagementResourceIT extends AbstractRestIT { String accessToken = postResponse2.getAccessToken(); assertNotNull( accessToken ); + //Superuser : GET -> get tokenInfo with access_token + ApiResponse getResponse3 = pathResource("management/me").get(ApiResponse.class,new QueryParameters() + .addParam("grant_type", "password").addParam("password", "superpassword") + .addParam("username", "superuser"),false); + + assertNotNull(getResponse3.getAccessToken()); + + //Superuser : POST -> Add org using super user credentials. + Map<String, Object> orgAdminUserInfo = new HashMap<String, Object>() {{ + put( "username", username+"test" ); + put("password","RandomPassword"); + put("email",username+"@gmail.com"); + put( "organization", username+"RandomOrgName" ); + }}; + ApiResponse postResponse4 = pathResource("management/orgs") + .post(false,orgAdminUserInfo,new QueryParameters().addParam("access_token",getResponse3.getAccessToken())); + assertNotNull(postResponse4.getData()); + + } finally { // turn off validate external tokens by un-setting the usergrid.central.url props.put( USERGRID_EXTERNAL_PROVIDER_URL, "" ); + props.put(USERGRID_EXTERNAL_SSO_ENABLED, ""); pathResource( "testproperties" ).post( props ); } } http://git-wip-us.apache.org/repos/asf/usergrid/blob/6d88bcfd/stack/services/src/main/java/org/apache/usergrid/security/sso/ApigeeSSO2Provider.java ---------------------------------------------------------------------- diff --git a/stack/services/src/main/java/org/apache/usergrid/security/sso/ApigeeSSO2Provider.java b/stack/services/src/main/java/org/apache/usergrid/security/sso/ApigeeSSO2Provider.java index 61a1601..d48495d 100644 --- a/stack/services/src/main/java/org/apache/usergrid/security/sso/ApigeeSSO2Provider.java +++ b/stack/services/src/main/java/org/apache/usergrid/security/sso/ApigeeSSO2Provider.java @@ -57,7 +57,7 @@ public class ApigeeSSO2Provider implements ExternalSSOProvider { protected Properties properties; protected ManagementService management; protected Client client; - protected String publicKey; + protected PublicKey publicKey; public static final String USERGRID_EXTERNAL_PUBLICKEY_URL = "usergrid.external.sso.url"; @@ -67,12 +67,17 @@ public class ApigeeSSO2Provider implements ExternalSSOProvider { client = ClientBuilder.newClient(clientConfig); } - public String getPublicKey(String keyUrl) { + public PublicKey getPublicKey(String keyUrl) { if(keyUrl != null && !keyUrl.isEmpty()) { try { Map<String, Object> publicKey = client.target(keyUrl).request().get(Map.class); - return publicKey.get(RESPONSE_PUBLICKEY_VALUE).toString().split("----\n")[1].split("\n---")[0]; + String ssoPublicKey = publicKey.get(RESPONSE_PUBLICKEY_VALUE).toString().split("----\n")[1].split("\n---")[0]; + byte[] publicBytes = decodeBase64(ssoPublicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey pubKey = keyFactory.generatePublic(keySpec); + return pubKey; } catch(Exception e){ throw new IllegalArgumentException("error getting public key"); @@ -140,7 +145,7 @@ public class ApigeeSSO2Provider implements ExternalSSOProvider { return properties.getProperty(USERGRID_EXTERNAL_PUBLICKEY_URL); } - public Jws<Claims> getClaimsForKeyUrl(String token, String ssoPublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, BadTokenException { + public Jws<Claims> getClaimsForKeyUrl(String token, PublicKey ssoPublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, BadTokenException { Jws<Claims> claims = null; if(ssoPublicKey == null){ @@ -148,14 +153,8 @@ public class ApigeeSSO2Provider implements ExternalSSOProvider { "token in order to verify signature."); } - - byte[] publicBytes = decodeBase64(ssoPublicKey); - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - PublicKey pubKey = keyFactory.generatePublic(keySpec); - try { - claims = Jwts.parser().setSigningKey(pubKey).parseClaimsJws(token); + claims = Jwts.parser().setSigningKey(ssoPublicKey).parseClaimsJws(token); } catch (SignatureException se) { if(logger.isDebugEnabled()) { logger.debug("Signature was invalid for Apigee JWT: {} and key: {}", token, ssoPublicKey); @@ -196,10 +195,13 @@ public class ApigeeSSO2Provider implements ExternalSSOProvider { throw new ExpiredTokenException(String.format("Token expired %d millisecons ago.", expirationDelta )); } - } + public void setPublicKey( PublicKey publicKeyArg){ + this.publicKey = publicKeyArg; + } + @Autowired public void setManagement(ManagementService management) { this.management = management; @@ -208,6 +210,6 @@ public class ApigeeSSO2Provider implements ExternalSSOProvider { @Autowired public void setProperties(Properties properties) { this.properties = properties; - this.publicKey = getPublicKey(getExternalSSOUrl()); + this.publicKey = getPublicKey(getExternalSSOUrl()); } } http://git-wip-us.apache.org/repos/asf/usergrid/blob/6d88bcfd/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 62d454a..46ea7fe 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 @@ -328,6 +328,9 @@ public class TokenServiceImpl implements TokenService { try{ uuid = getUUIDForToken( token ); } + catch (ExpiredTokenException expiredTokenException){ + throw new ExpiredTokenException(expiredTokenException.getMessage()); + } catch(Exception e){ // If the token doesn't parse as a Usergrid token, see if an external provider other than Usergrid is