You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by gr...@apache.org on 2015/04/16 20:09:25 UTC
[1/9] incubator-usergrid git commit: First cut of Usergrid Central
SSO feature, with tests.
Repository: incubator-usergrid
Updated Branches:
refs/heads/master 6d962b7fe -> 25d0077ed
First cut of Usergrid Central SSO feature, with tests.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/098355f8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/098355f8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/098355f8
Branch: refs/heads/master
Commit: 098355f8521a7528fe0fa44e9ce959f7c4c603d8
Parents: 9ef73e7
Author: Dave Johnson <dm...@apigee.com>
Authored: Mon Apr 13 11:24:53 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Mon Apr 13 11:24:53 2015 -0400
----------------------------------------------------------------------
.../rest/management/ManagementResource.java | 170 ++++++++++++++++++-
.../OAuth2AccessTokenSecurityFilter.java | 5 +
.../rest/management/ManagementResourceIT.java | 71 +++++++-
.../rest/test/PropertiesResourceIT.java | 23 +--
.../usergrid/management/ManagementService.java | 5 +-
.../cassandra/ManagementServiceImpl.java | 7 +
.../apache/usergrid/security/shiro/Realm.java | 10 +-
.../usergrid/security/tokens/TokenService.java | 3 +
.../tokens/cassandra/TokenServiceImpl.java | 44 ++++-
.../security/tokens/TokenServiceIT.java | 42 +++++
10 files changed, 354 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/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 c590140..c1c0be5 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
@@ -19,21 +19,25 @@ package org.apache.usergrid.rest.management;
import java.net.URLEncoder;
import java.util.Map;
+import java.util.UUID;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.FormParam;
-import javax.ws.rs.GET;
-import javax.ws.rs.HeaderParam;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.json.JSONConfiguration;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.usergrid.management.OrganizationInfo;
+import org.apache.usergrid.management.OrganizationOwnerInfo;
+import org.apache.usergrid.persistence.exceptions.EntityNotFoundException;
+import org.apache.usergrid.rest.security.annotations.RequireSystemAccess;
+import org.apache.usergrid.utils.UUIDUtils;
+import org.codehaus.jackson.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
@@ -95,6 +99,8 @@ public class ManagementResource extends AbstractContextResource {
private static final Logger logger = LoggerFactory.getLogger( ManagementResource.class );
+ public static final String USERGRID_CENTRAL_URL = "usergrid.central.url";
+
public ManagementResource() {
logger.info( "ManagementResource initialized" );
@@ -441,6 +447,152 @@ public class ManagementResource extends AbstractContextResource {
}
+ /**
+ * <p>
+ * Validates access token from other or "external" Usergrid system.
+ * Calls other system's /management/me endpoint to get the User associated with the access token.
+ * If user does not exist locally, then user and organization with the same name of user is created.
+ * If no user is returned from the other cluster, then this endpoint will return 401.
+ * <p>
+ *
+ * <p>
+ * See <a href="https://issues.apache.org/jira/browse/USERGRID-567">USERGRID-567</a>
+ * for details about Usergrid Central SSO.
+ * </p>
+ *
+ * @param ui Information about calling URI.
+ * @param json JSON object with fields: ext_access_token, ttl
+ * @param callback For JSONP support.
+ * @return Returns JSON object with access_token field.
+ * @throws Exception Returns 401 if access token cannot be validated
+ */
+ @POST
+ @Path( "/externaltoken" )
+ //@RequireSystemAccess TODO: should this be required
+ public Response validateExternalToken(
+ @Context UriInfo ui,
+ Map<String, Object> json,
+ @QueryParam( "callback" ) @DefaultValue( "" ) String callback ) throws Exception {
+
+ Object extAccessTokenObj = json.get("ext_access_token");
+ if ( extAccessTokenObj == null ) {
+ throw new IllegalArgumentException("ext_access_token must be specified");
+ }
+ String extAccessToken = json.get("ext_access_token").toString();
+
+ Object ttlObj = json.get("ttl");
+ if ( ttlObj == null ) {
+ throw new IllegalArgumentException("ttl must be specified");
+ }
+ long ttl;
+ try {
+ ttl = Long.parseLong(ttlObj.toString());
+ } catch ( NumberFormatException e ) {
+ throw new IllegalArgumentException("ttl must be specified as a long");
+ }
+
+ return validateExternalToken( ui, extAccessToken, ttl, callback );
+ }
+
+
+ /**
+ * <p>
+ * Validates access token from other or "external" Usergrid system.
+ * Calls other system's /management/me endpoint to get the User associated with the access token.
+ * If user does not exist locally, then user and organization with the same name of user is created.
+ * If no user is returned from the other cluster, then this endpoint will return 401.
+ * </p>
+ *
+ * <p> Part of Usergrid Central SSO feature.
+ * See <a href="https://issues.apache.org/jira/browse/USERGRID-567">USERGRID-567</a>
+ * for details about Usergrid Central SSO.
+ * </p>
+ *
+ * @param ui Information about calling URI.
+ * @param extAccessToken Access token from external Usergrid system.
+ * @param ttl Time to live for token.
+ * @param callback For JSONP support.
+ * @return Returns JSON object with access_token field.
+ * @throws Exception Returns 401 if access token cannot be validated
+ */
+ @GET
+ @Path( "/externaltoken" )
+ //@RequireSystemAccess TODO: should this be required
+ public Response validateExternalToken(
+ @Context UriInfo ui,
+ @QueryParam( "ext_access_token" ) String extAccessToken,
+ @QueryParam( "ttl" ) @DefaultValue("-1") long ttl,
+ @QueryParam( "callback" ) @DefaultValue( "" ) String callback )
+ throws Exception {
+
+
+ if ( extAccessToken == null ) {
+ throw new IllegalArgumentException("ext_access_token must be specified");
+ }
+
+ if ( ttl == -1 ) {
+ throw new IllegalArgumentException("ttl must be specified");
+ }
+
+ // 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
+
+ ClientConfig clientConfig = new DefaultClientConfig();
+ clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
+ Client client = Client.create(clientConfig);
+ final JsonNode accessInfoNode;
+ try {
+ accessInfoNode = client.resource( me )
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .get(JsonNode.class);
+
+ } catch ( Exception e ) {
+ // user not found 404
+ String msg = "Cannot find Admin User associated with " + extAccessToken;
+ throw new EntityNotFoundException( msg, e );
+ }
+
+ // good. user was found in central Usergrid
+
+ JsonNode userNode = accessInfoNode.get( "user" );
+ UUID userId = UUIDUtils.tryExtractUUID( userNode.get( "uuid" ).getTextValue() );
+ String username = userNode.get( "username" ).getTextValue();
+ String name = userNode.get( "name" ).getTextValue();
+ String email = userNode.get( "email" ).getTextValue();
+
+ // set dummy password to random string that nobody can guess, in SSO setup
+ // admin users should never be able to login directly to this Usergrid system
+ String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 );
+
+ // if user does not exist locally then we need to fix that
+
+ final OrganizationInfo organizationInfo = management.getOrganizationByName(username);
+ if ( organizationInfo == null ) {
+
+ // create local user and personal organization, activate user.
+
+ OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization(
+ username, username, name, email, dummyPassword, true, true);
+ }
+
+ // store the external access_token as if it were one of our own
+ management.importTokenForAdminUser( userId, extAccessToken, ttl );
+
+ // success! return JSON object with access_token field
+ AccessInfo accessInfo = new AccessInfo()
+ .withExpiresIn( tokens.getMaxTokenAgeInSeconds(extAccessToken ) )
+ .withAccessToken( extAccessToken );
+
+ return Response.status( SC_OK ).type( jsonMediaType( callback ) ).entity( accessInfo ).build();
+ }
+
+
String errorMsg = "";
String responseType;
String clientId;
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/rest/src/main/java/org/apache/usergrid/rest/security/shiro/filters/OAuth2AccessTokenSecurityFilter.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/main/java/org/apache/usergrid/rest/security/shiro/filters/OAuth2AccessTokenSecurityFilter.java b/stack/rest/src/main/java/org/apache/usergrid/rest/security/shiro/filters/OAuth2AccessTokenSecurityFilter.java
index f4c4fc5..e3a7d54 100644
--- a/stack/rest/src/main/java/org/apache/usergrid/rest/security/shiro/filters/OAuth2AccessTokenSecurityFilter.java
+++ b/stack/rest/src/main/java/org/apache/usergrid/rest/security/shiro/filters/OAuth2AccessTokenSecurityFilter.java
@@ -104,7 +104,12 @@ public class OAuth2AccessTokenSecurityFilter extends SecurityFilter {
catch ( InvalidTokenException ite ) {
throw mappableSecurityException( INVALID_AUTH_ERROR );
}
+ catch ( IndexOutOfBoundsException ioobe ) {
+ // token is just some rubbish string
+ throw mappableSecurityException( BAD_ACCESS_TOKEN_ERROR );
+ }
catch ( Exception e ) {
+ // unexpected so we log it
LOG.error( "unable to verify oauth token", e );
throw mappableSecurityException( UNVERIFIED_OAUTH_ERROR );
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/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 13cb2ae..b18614d 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
@@ -26,6 +26,7 @@ import java.util.UUID;
import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang.RandomStringUtils;
import org.codehaus.jackson.JsonNode;
import org.junit.Test;
@@ -40,12 +41,13 @@ import org.apache.usergrid.rest.management.organizations.OrganizationsResource;
import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.representation.Form;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.apache.usergrid.utils.MapUtils.hashMap;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+
+import static org.apache.usergrid.rest.management.ManagementResource.USERGRID_CENTRAL_URL;
+import static org.junit.Assert.*;
/**
@@ -54,6 +56,8 @@ import static org.junit.Assert.assertTrue;
@Concurrent()
public class ManagementResourceIT extends AbstractRestIT {
+ private static final Logger logger = LoggerFactory.getLogger(ManagementResourceIT.class);
+
public ManagementResourceIT() throws Exception {
}
@@ -640,4 +644,63 @@ public class ManagementResourceIT extends AbstractRestIT {
assertEquals( Status.OK, status );
}
+
+ @Test
+ public void validateExternalToken() throws Exception {
+
+ // set the Usergrid Central SSO URL becuase Tomcat port is dynamically assigned
+
+ String suToken = superAdminToken();
+
+ Map<String, String> props = new HashMap<String, String>();
+ props.put( USERGRID_CENTRAL_URL, getBaseURI().toURL().toExternalForm());
+ resource().path( "/testproperties" )
+ .queryParam( "access_token", suToken)
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( props );
+
+ // create a new admin user, get access token
+
+ String rand = RandomStringUtils.randomAlphanumeric(10);
+ final String username = "user_" + rand;
+ OrganizationOwnerInfo orgInfo = setup.getMgmtSvc().createOwnerAndOrganization(
+ username, username, "Test User", username + "@example.com", "password" );
+
+ Map<String, Object> loginInfo = new HashMap<String, Object>() {{
+ put("username", username );
+ put("password", "password");
+ put("grant_type", "password");
+ }};
+ JsonNode accessInfoNode = resource().path("/management/token")
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( JsonNode.class, loginInfo );
+ String accessToken = accessInfoNode.get( "access_token" ).getTextValue();
+
+ // attempt to validate the token, must be valid
+
+ JsonNode validatedNode = resource().path( "/management/externaltoken" )
+ .queryParam( "access_token", suToken ) // as superuser
+ .queryParam( "ext_access_token", accessToken )
+ .queryParam( "ttl", "1000" )
+ .get( JsonNode.class );
+ String validatedAccessToken = validatedNode.get( "access_token" ).getTextValue();
+ assertEquals( accessToken, validatedAccessToken );
+
+ // attempt to validate an invalid token, must fail
+
+ try {
+ JsonNode invalidNode = resource().path( "/management/externaltoken" )
+ .queryParam( "access_token", suToken ) // as superuser
+ .queryParam( "ext_access_token", "rubbish_token")
+ .queryParam( "ttl", "1000" )
+ .get( JsonNode.class );
+ fail("Validation should have failed");
+ } catch ( Exception expected ) {}
+
+
+ // TODO: how do we test the create new user and organization case?
+
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/rest/src/test/java/org/apache/usergrid/rest/test/PropertiesResourceIT.java
----------------------------------------------------------------------
diff --git a/stack/rest/src/test/java/org/apache/usergrid/rest/test/PropertiesResourceIT.java b/stack/rest/src/test/java/org/apache/usergrid/rest/test/PropertiesResourceIT.java
index ebd6f9e..db3f046 100644
--- a/stack/rest/src/test/java/org/apache/usergrid/rest/test/PropertiesResourceIT.java
+++ b/stack/rest/src/test/java/org/apache/usergrid/rest/test/PropertiesResourceIT.java
@@ -36,9 +36,6 @@ import static org.apache.usergrid.management.AccountCreationProps.PROPERTIES_ADM
public class PropertiesResourceIT extends AbstractRestIT {
static final Logger logger = LoggerFactory.getLogger( PropertiesResourceIT.class );
- @Autowired
- protected ServiceManagerFactory smf;
-
@Test
public void testBasicOperation() {
@@ -51,25 +48,31 @@ public class PropertiesResourceIT extends AbstractRestIT {
// verify that is is not set in Jetty
{
- Map<String, String> map = resource().path( "/testproperties" ).queryParam( "access_token", access_token )
- .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).get( Map.class );
- Assert.assertFalse( Boolean.parseBoolean( map.get( PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION ) ) );
+ Map map = resource().path( "/testproperties" )
+ .queryParam( "access_token", access_token )
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE ).get(Map.class);
+ Assert.assertFalse( Boolean.parseBoolean(
+ map.get( PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION ).toString() ) );
}
// set property in Jetty
{
Map<String, String> props = new HashMap<String, String>();
props.put( PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION, "true" );
- resource().path( "/testproperties" ).queryParam( "access_token", access_token )
- .accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).post( props );
+ resource().path( "/testproperties" )
+ .queryParam( "access_token", access_token )
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE ).post( props );
}
// verify that it is set in Jetty
{
- Map<String, String> map = resource().path( "/testproperties" ).queryParam( "access_token", access_token )
+ Map map = resource().path( "/testproperties" ).queryParam( "access_token", access_token )
.accept( MediaType.APPLICATION_JSON ).type( MediaType.APPLICATION_JSON_TYPE ).get( Map.class );
- Assert.assertTrue( Boolean.parseBoolean( map.get( PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION ) ) );
+ Assert.assertTrue( Boolean.parseBoolean(
+ map.get( PROPERTIES_ADMIN_USERS_REQUIRE_CONFIRMATION ).toString() ) );
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java b/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
index 865f296..56343d4 100644
--- a/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
+++ b/stack/services/src/main/java/org/apache/usergrid/management/ManagementService.java
@@ -109,7 +109,7 @@ public interface ManagementService {
public String getAccessTokenForAdminUser( UUID userId, long duration ) throws Exception;
- /** Revoke all active access tokens for this admin user */
+ /** Revoke all active access tokens for this admin user */
public void revokeAccessTokensForAdminUser( UUID userId ) throws Exception;
public void revokeAccessTokenForAdminUser( UUID userId, String token ) throws Exception;
@@ -120,6 +120,9 @@ public interface ManagementService {
public String getActivationTokenForOrganization( UUID organizationId, long ttl ) throws Exception;
+ /** Import an Admin User token generated by some other system */
+ public void importTokenForAdminUser( UUID userId, String token, long ttl ) throws Exception;
+
public ServiceResults getAdminUserActivities( UserInfo user ) throws Exception;
public ServiceResults getAdminUserActivity( UserInfo user ) throws Exception;
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java b/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
index a2e1271..5b77534 100644
--- a/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
+++ b/stack/services/src/main/java/org/apache/usergrid/management/cassandra/ManagementServiceImpl.java
@@ -1391,6 +1391,13 @@ public class ManagementServiceImpl implements ManagementService {
}
+ @Override
+ public void importTokenForAdminUser(UUID userId, String token, long ttl) throws Exception {
+ tokens.importToken( token, TokenCategory.ACCESS, null,
+ new AuthPrincipalInfo( ADMIN_USER, userId, MANAGEMENT_APPLICATION_ID ), null, ttl );
+ }
+
+
/*
* (non-Javadoc)
*
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/services/src/main/java/org/apache/usergrid/security/shiro/Realm.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/security/shiro/Realm.java b/stack/services/src/main/java/org/apache/usergrid/security/shiro/Realm.java
index 84f4fe5..0f1f9e8 100644
--- a/stack/services/src/main/java/org/apache/usergrid/security/shiro/Realm.java
+++ b/stack/services/src/main/java/org/apache/usergrid/security/shiro/Realm.java
@@ -235,8 +235,16 @@ public class Realm extends AuthorizingRealm {
for ( PrincipalIdentifier principal : principals.byType( PrincipalIdentifier.class ) ) {
+ // TODO: refactor so that this whole instanceof mess can be replaced with this:
+ // principle.addRolesAndPermissionsToInfo( info );
+ // principle.addApplicationsToSet( applicationsSet );
+ // principle.addOrganizationsToSet( applicationsSet );
+ // organization = principle.getOrganization();
+ // application = principle.getApplication();
+
if ( principal instanceof OrganizationPrincipal ) {
- // OrganizationPrincipals are usually only through OAuth
+
+ // OrganizationPricipals are usually only through OAuth
// They have access to a single organization
organization = ( ( OrganizationPrincipal ) principal ).getOrganization();
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/services/src/main/java/org/apache/usergrid/security/tokens/TokenService.java
----------------------------------------------------------------------
diff --git a/stack/services/src/main/java/org/apache/usergrid/security/tokens/TokenService.java b/stack/services/src/main/java/org/apache/usergrid/security/tokens/TokenService.java
index 8b5863f..36decce 100644
--- a/stack/services/src/main/java/org/apache/usergrid/security/tokens/TokenService.java
+++ b/stack/services/src/main/java/org/apache/usergrid/security/tokens/TokenService.java
@@ -31,6 +31,9 @@ public interface TokenService {
public String createToken( TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
Map<String, Object> state, long duration ) throws Exception;
+ public void importToken( String token, TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+ Map<String, Object> state, long duration ) throws Exception;
+
/** Get the token info for the string version of this token */
public TokenInfo getTokenInfo( String token ) throws Exception;
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/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 50156b5..7ea2af3 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
@@ -230,14 +230,56 @@ public class TokenServiceImpl implements TokenService {
Assert.notNull( principal.getUuid() );
}
+ // create UUID that we will use to store token info in our database
UUID uuid = UUIDUtils.newTimeUUID( creationTimestamp );
+
long timestamp = getTimestampInMillis( uuid );
if ( type == null ) {
type = TOKEN_TYPE_ACCESS;
}
TokenInfo tokenInfo = new TokenInfo( uuid, type, timestamp, timestamp, 0, duration, principal, state );
putTokenInfo( tokenInfo );
- return getTokenForUUID( tokenInfo, tokenCategory, uuid );
+
+ // generate token from the UUID that we created
+ return getTokenForUUID(tokenInfo, tokenCategory, uuid);
+ }
+
+
+ @Override
+ public void importToken(String token, TokenCategory tokenCategory, String type, AuthPrincipalInfo principal,
+ Map<String, Object> state, long duration) throws Exception {
+
+ // same logic as create token
+
+ long maxTokenTtl = getMaxTtl( tokenCategory, principal );
+
+ if ( duration > maxTokenTtl ) {
+ throw new IllegalArgumentException(
+ String.format( "Your token age cannot be more than the maximum age of %d milliseconds",
+ maxTokenTtl ) );
+ }
+
+ if ( duration == 0 ) {
+ duration = maxTokenTtl;
+ }
+
+ if ( principal != null ) {
+ Assert.notNull( principal.getType() );
+ Assert.notNull( principal.getApplicationId() );
+ Assert.notNull( principal.getUuid() );
+ }
+
+ // except that we generate the UUID based on the token
+
+ UUID uuid = getUUIDForToken(token);
+
+ long timestamp = getTimestampInMillis( uuid );
+ if ( type == null ) {
+ type = TOKEN_TYPE_ACCESS;
+ }
+
+ TokenInfo tokenInfo = new TokenInfo( uuid, type, timestamp, timestamp, 0, duration, principal, state );
+ putTokenInfo( tokenInfo );
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/098355f8/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
----------------------------------------------------------------------
diff --git a/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java b/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
index 0141c9b..58ab3e6 100644
--- a/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
+++ b/stack/services/src/test/java/org/apache/usergrid/security/tokens/TokenServiceIT.java
@@ -454,4 +454,46 @@ public class TokenServiceIT {
assertTrue( invalidTokenException );
}
+
+ @Test
+ public void testImportToken() throws Exception {
+
+ // create admin user token and make sure it is working
+
+ AuthPrincipalInfo adminPrincipal = new AuthPrincipalInfo(
+ AuthPrincipalType.ADMIN_USER, adminUser.getUuid(), UUIDUtils.newTimeUUID() );
+
+ String tokenStr = setup.getTokenSvc().createToken(
+ TokenCategory.ACCESS, null, adminPrincipal, null, 0 );
+
+ log.info("token: " + tokenStr);
+
+ // revoke token and check to make sure it is no longer valid
+
+ setup.getTokenSvc().revokeToken( tokenStr );
+
+ boolean invalidTokenException = false;
+ try {
+ setup.getTokenSvc().getTokenInfo( tokenStr );
+ }
+ catch ( InvalidTokenException ite ) {
+ invalidTokenException = true;
+ }
+ assertTrue(invalidTokenException);
+
+ // import same token and make sure it works again
+
+ setup.getTokenSvc().importToken( tokenStr, TokenCategory.ACCESS, null, adminPrincipal, null, 0 );
+
+ TokenInfo tokenInfo = setup.getTokenSvc().getTokenInfo( tokenStr );
+
+ long last_access = tokenInfo.getAccessed();
+
+ assertEquals( "access", tokenInfo.getType() );
+ assertEquals( adminUser.getUuid(), tokenInfo.getPrincipal().getUuid() );
+
+ tokenInfo = setup.getTokenSvc().getTokenInfo( tokenStr );
+
+ assertTrue(last_access < tokenInfo.getAccessed());
+ }
}
[9/9] incubator-usergrid git commit: An additional test for validate
external tokens logic, plus some related fixes to superuser-only login logic.
Posted by gr...@apache.org.
An additional test for validate external tokens logic, plus some related fixes to superuser-only login logic.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/25d0077e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/25d0077e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/25d0077e
Branch: refs/heads/master
Commit: 25d0077edd0b69607459ef3b0c87d276498adac9
Parents: 6ae492e
Author: Dave Johnson <dm...@apigee.com>
Authored: Thu Apr 16 11:22:07 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Thu Apr 16 11:22:07 2015 -0400
----------------------------------------------------------------------
.../rest/management/ManagementResource.java | 161 ++++++++++---------
.../rest/management/ManagementResourceIT.java | 108 +++++++++++--
2 files changed, 176 insertions(+), 93 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/25d0077e/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 73963fe..2bbcfd9 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
@@ -37,6 +37,7 @@ import org.apache.usergrid.exception.NotImplementedException;
import org.apache.usergrid.management.ApplicationCreator;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.OrganizationOwnerInfo;
+import org.apache.usergrid.persistence.entities.User;
import org.apache.usergrid.persistence.exceptions.EntityNotFoundException;
import org.codehaus.jackson.JsonNode;
import org.slf4j.Logger;
@@ -87,6 +88,8 @@ import static org.apache.usergrid.utils.StringUtils.stringOrSubstringBeforeFirst
} )
public class ManagementResource extends AbstractContextResource {
+ private static final Logger logger = LoggerFactory.getLogger( ManagementResource.class );
+
/*-
* New endpoints:
*
@@ -99,32 +102,17 @@ public class ManagementResource extends AbstractContextResource {
*
*/
- private static final Logger logger = LoggerFactory.getLogger( ManagementResource.class );
-
@Autowired
private ApplicationCreator applicationCreator;
public static final String USERGRID_CENTRAL_URL = "usergrid.central.url";
- private boolean superuserAllowed;
-
- private boolean externalTokensEnabled;
+ public static final String USERGRID_SYSADMIN_LOGIN_NAME = "usergrid.sysadmin.login.name";
- private String superuserName;
+ public static final String USERGRID_SYSADMIN_LOGIN_ALLOWED = "usergrid.sysadmin.login.allowed";
public ManagementResource() {
-
- String superuserAllowedStr = properties.getProperty( "usergrid.sysadmin.login.allowed" );
-
- superuserName = properties.getProperty( "usergrid.sysadmin.login.name" );
-
- superuserAllowed = !StringUtils.isEmpty( superuserAllowedStr )
- && superuserAllowedStr.trim().equalsIgnoreCase( "true" );
-
- externalTokensEnabled =
- !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ));
-
logger.info( "ManagementResource initialized" );
}
@@ -196,20 +184,11 @@ public class ManagementResource extends AbstractContextResource {
}
- private Response getAccessTokenInternal( UriInfo ui, String authorization, String grant_type, String username,
+ private Response getAccessTokenInternal( UriInfo ui, String authorization, String grant_type, String username,
String password, String client_id, String client_secret, long ttl,
String callback, boolean loadAdminData ) throws Exception {
- // when external tokens (Usergrid Central SSO) are enabled only superuser can login as Admin User.
- if ( externalTokensEnabled &&
- superuserAllowed && !username.equalsIgnoreCase( superuserName )) {
-
- // cause an HTTP 400 response with a useful message
- throw new IllegalArgumentException("Admin Users must login via " +
- properties.getProperty( USERGRID_CENTRAL_URL ));
- }
-
UserInfo user = null;
try {
@@ -222,6 +201,11 @@ public class ManagementResource extends AbstractContextResource {
String errorDescription = "invalid username or password";
if ( user == null ) {
+
+ // make sure authentication is allowed considering
+ // external token validation configuration (UG Central SSO)
+ ensureAuthenticationAllowed( username, grant_type );
+
if ( authorization != null ) {
String type = stringOrSubstringBeforeFirst( authorization, ' ' ).toUpperCase();
@@ -482,15 +466,7 @@ public class ManagementResource extends AbstractContextResource {
/**
* <p>
- * Validates access token from other or "external" Usergrid system.
- * Calls other system's /management/me endpoint to get the User associated with the access token.
- * If user does not exist locally, then user and organization with the same name of user is created.
- * If no user is returned from the other cluster, then this endpoint will return 401.
- * <p>
- *
- * <p>
- * See <a href="https://issues.apache.org/jira/browse/USERGRID-567">USERGRID-567</a>
- * for details about Usergrid Central SSO.
+ * Allows call to validateExternalToken() (see below) with a POST of a JSON object.
* </p>
*
* @param ui Information about calling URI.
@@ -573,71 +549,74 @@ public class ManagementResource extends AbstractContextResource {
if ( ttl == -1 ) {
throw new IllegalArgumentException("ttl must be specified");
}
+ AccessInfo accessInfo = null;
- // look up user via UG Central's /management/me endpoint.
-
- JsonNode accessInfoNode = getMeFromUgCentral( extAccessToken );
-
- JsonNode userNode = accessInfoNode.get( "user" );
- String username = userNode.get( "username" ).getTextValue();
+ try {
+ // look up user via UG Central's /management/me endpoint.
- // if user does not exist locally then we need to fix that
+ JsonNode accessInfoNode = getMeFromUgCentral( extAccessToken );
- UUID userId = management.getAdminUserByUsername( username ).getUuid();
+ JsonNode userNode = accessInfoNode.get( "user" );
+ String username = userNode.get( "username" ).getTextValue();
- if ( userId == null ) {
+ // if user does not exist locally then we need to fix that
- // create local user and and organizations they have on the central Usergrid instance
+ UserInfo userInfo = management.getAdminUserByUsername( username );
+ UUID userId = userInfo == null ? null : userInfo.getUuid();
- String name = userNode.get( "name" ).getTextValue();
- String email = userNode.get( "email" ).getTextValue();
+ if ( userId == null ) {
- // set dummy password to random string that nobody can guess, in central SSO setup
- // admin users should never be able to login directly to this Usergrid system
- String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 );
+ // create local user and and organizations they have on the central Usergrid instance
- JsonNode orgsNode = userNode.get( "organizations" );
- final Iterator<String> fieldNames = orgsNode.getFieldNames();
+ String name = userNode.get( "name" ).getTextValue();
+ String email = userNode.get( "email" ).getTextValue();
+ String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 );
- UserInfo userInfo = null;
+ JsonNode orgsNode = userNode.get( "organizations" );
+ final Iterator<String> fieldNames = orgsNode.getFieldNames();
- // create user and any organizations that user is supposed to have
+ // create user and any organizations that user is supposed to have
- while ( fieldNames.hasNext() ) {
+ while ( fieldNames.hasNext() ) {
- String orgName = fieldNames.next();
+ String orgName = fieldNames.next();
- if ( userId == null ) {
+ if ( userId == null ) {
- // haven't created user yet so do that now
- OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization(
- orgName, username, name, email, dummyPassword, true, true );
+ // haven't created user yet so do that now
+ OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization(
+ orgName, username, name, email, dummyPassword, true, true );
- management.activateOrganization( ownerOrgInfo.getOrganization() ); // redundant?
- applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() );
+ management.activateOrganization( ownerOrgInfo.getOrganization() ); // redundant?
+ applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() );
- userId = ownerOrgInfo.getOwner().getUuid();
- userInfo = ownerOrgInfo.getOwner();
+ userId = ownerOrgInfo.getOwner().getUuid();
+ userInfo = ownerOrgInfo.getOwner();
- } else {
+ } else {
- // already created user, so just create an org
- final OrganizationInfo organization = management.createOrganization( orgName, userInfo, true );
+ // already created user, so just create an org
+ final OrganizationInfo organization = management.createOrganization( orgName, userInfo, true );
- management.activateOrganization( organization ); // redundant?
- applicationCreator.createSampleFor( organization );
+ management.activateOrganization( organization ); // redundant?
+ applicationCreator.createSampleFor( organization );
+ }
}
+
}
- }
+ // store the external access_token as if it were one of our own
+ management.importTokenForAdminUser( userId, extAccessToken, ttl );
- // store the external access_token as if it were one of our own
- management.importTokenForAdminUser( userId, extAccessToken, ttl );
+ // success! return JSON object with access_token field
+ accessInfo = new AccessInfo()
+ .withExpiresIn( tokens.getMaxTokenAgeInSeconds( extAccessToken ) )
+ .withAccessToken( extAccessToken );
- // success! return JSON object with access_token field
- AccessInfo accessInfo = new AccessInfo()
- .withExpiresIn( tokens.getMaxTokenAgeInSeconds(extAccessToken ) )
- .withAccessToken( extAccessToken );
+ } catch (Exception e) {
+ logger.debug("Error validating external token", e);
+ throw e;
+ }
return Response.status( SC_OK ).type( jsonMediaType( callback ) ).entity( accessInfo ).build();
}
@@ -680,6 +659,34 @@ public class ManagementResource extends AbstractContextResource {
}
+ /**
+ * Check that authentication is allowed. If external token validation is enabled (Central Usergrid SSO)
+ * then only superusers should be allowed to login directly to this Usergrid instance.
+ */
+ private void ensureAuthenticationAllowed( String username, String grant_type ) {
+
+ final boolean externalTokensEnabled =
+ !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ) );
+
+ if ( externalTokensEnabled ) {
+
+ // when external tokens enabled then only superuser can obtain an access token
+
+ final String superuserName = properties.getProperty( USERGRID_SYSADMIN_LOGIN_NAME );
+ final String superuserAllowedStr = properties.getProperty( USERGRID_SYSADMIN_LOGIN_ALLOWED );
+ final boolean superuserAllowed = !StringUtils.isEmpty( superuserAllowedStr )
+ && superuserAllowedStr.trim().equalsIgnoreCase( "true" );
+
+ if ( superuserAllowed && !superuserName.equalsIgnoreCase( username )) {
+
+ // this guy is not the superuser
+ throw new IllegalArgumentException( "Admin Users must login via " +
+ properties.getProperty( USERGRID_CENTRAL_URL ) );
+ }
+ }
+ }
+
+
String errorMsg = "";
String responseType;
String clientId;
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/25d0077e/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 b18614d..d6b507e 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
@@ -381,7 +381,7 @@ public class ManagementResourceIT extends AbstractRestIT {
assertEquals( "test@usergrid.com", userdata.get( "data" ).get( "email" ).asText() );
// wait for the token to expire
- Thread.sleep( ttl - ( System.currentTimeMillis() - startTime ) + 1000 );
+ Thread.sleep( ttl - (System.currentTimeMillis() - startTime) + 1000 );
Status responseStatus = null;
try {
@@ -646,19 +646,7 @@ public class ManagementResourceIT extends AbstractRestIT {
@Test
- public void validateExternalToken() throws Exception {
-
- // set the Usergrid Central SSO URL becuase Tomcat port is dynamically assigned
-
- String suToken = superAdminToken();
-
- Map<String, String> props = new HashMap<String, String>();
- props.put( USERGRID_CENTRAL_URL, getBaseURI().toURL().toExternalForm());
- resource().path( "/testproperties" )
- .queryParam( "access_token", suToken)
- .accept( MediaType.APPLICATION_JSON )
- .type( MediaType.APPLICATION_JSON_TYPE )
- .post( props );
+ public void testValidateExternalToken() throws Exception {
// create a new admin user, get access token
@@ -677,6 +665,17 @@ public class ManagementResourceIT extends AbstractRestIT {
.post( JsonNode.class, loginInfo );
String accessToken = accessInfoNode.get( "access_token" ).getTextValue();
+ // set the Usergrid Central SSO URL because Tomcat port is dynamically assigned
+
+ String suToken = superAdminToken();
+ Map<String, String> props = new HashMap<String, String>();
+ props.put( USERGRID_CENTRAL_URL, getBaseURI().toURL().toExternalForm());
+ resource().path( "/testproperties" )
+ .queryParam( "access_token", suToken)
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( props );
+
// attempt to validate the token, must be valid
JsonNode validatedNode = resource().path( "/management/externaltoken" )
@@ -690,17 +689,94 @@ public class ManagementResourceIT extends AbstractRestIT {
// attempt to validate an invalid token, must fail
try {
- JsonNode invalidNode = resource().path( "/management/externaltoken" )
+ resource().path( "/management/externaltoken" )
.queryParam( "access_token", suToken ) // as superuser
.queryParam( "ext_access_token", "rubbish_token")
.queryParam( "ttl", "1000" )
.get( JsonNode.class );
fail("Validation should have failed");
- } catch ( Exception expected ) {}
+ } catch ( Exception actual ) {
+ logger.debug( "error", actual );
+ }
+
// TODO: how do we test the create new user and organization case?
+
+
+ // unset the Usergrid Central SSO URL so it does not interfere with other tests
+
+ props.put( USERGRID_CENTRAL_URL, "" );
+ resource().path( "/testproperties" )
+ .queryParam( "access_token", suToken)
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( props );
+
+ }
+
+
+ @Test
+ public void testSuperuserOnlyWhenValidateExternalTokensEnabled() throws Exception {
+
+ // create an org and an admin user
+
+ String rand = RandomStringUtils.randomAlphanumeric(10);
+ final String username = "user_" + rand;
+ OrganizationOwnerInfo orgInfo = setup.getMgmtSvc().createOwnerAndOrganization(
+ username, username, "Test User", username + "@example.com", "password" );
+
+ // turn on validate external tokens by setting the usergrid.central.url
+
+ String suToken = superAdminToken();
+ Map<String, String> props = new HashMap<String, String>();
+ props.put( USERGRID_CENTRAL_URL, getBaseURI().toURL().toExternalForm());
+ resource().path( "/testproperties" )
+ .queryParam( "access_token", suToken)
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( props );
+
+ // calls to login as an Admin User must now fail
+
+ try {
+
+ Map<String, Object> loginInfo = new HashMap<String, Object>() {{
+ put("username", username );
+ put("password", "password");
+ put("grant_type", "password");
+ }};
+ JsonNode accessInfoNode = resource().path("/management/token")
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( JsonNode.class, loginInfo );
+ fail("Login as Admin User must fail when validate external tokens is enabled");
+
+ } catch ( Exception actual ) {
+ logger.debug( "error", actual );
+ }
+
+ // login as superuser must succeed
+
+ Map<String, Object> loginInfo = new HashMap<String, Object>() {{
+ put("username", "superuser");
+ put("password", "superpassword");
+ put("grant_type", "password");
+ }};
+ JsonNode accessInfoNode = resource().path("/management/token")
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( JsonNode.class, loginInfo );
+ String accessToken = accessInfoNode.get( "access_token" ).getTextValue();
+ assertNotNull( accessToken );
+
+ // turn off validate external tokens by un-setting the usergrid.central.url
+
+ props.put( USERGRID_CENTRAL_URL, "" );
+ resource().path( "/testproperties" )
+ .queryParam( "access_token", suToken)
+ .accept( MediaType.APPLICATION_JSON )
+ .type( MediaType.APPLICATION_JSON_TYPE )
+ .post( props );
}
}
[7/9] incubator-usergrid git commit: Merge branch 'master' of
https://github.com/snoopdave/incubator-usergrid
Posted by gr...@apache.org.
Merge branch 'master' of https://github.com/snoopdave/incubator-usergrid
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/7aabf68f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/7aabf68f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/7aabf68f
Branch: refs/heads/master
Commit: 7aabf68f24d2d88bf3bd0fbc5afcb6d8257e83f0
Parents: 6d962b7 9a8d5e0
Author: Dave Johnson <dm...@apigee.com>
Authored: Thu Apr 16 09:40:33 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Thu Apr 16 09:40:33 2015 -0400
----------------------------------------------------------------------
.../main/resources/usergrid-default.properties | 5 +
.../rest/management/ManagementResource.java | 260 ++++++++++++++++++-
.../OAuth2AccessTokenSecurityFilter.java | 5 +
.../rest/management/ManagementResourceIT.java | 71 ++++-
.../rest/test/PropertiesResourceIT.java | 23 +-
.../usergrid/management/ManagementService.java | 5 +-
.../cassandra/ManagementServiceImpl.java | 7 +
.../apache/usergrid/security/shiro/Realm.java | 10 +-
.../usergrid/security/tokens/TokenService.java | 3 +
.../tokens/cassandra/TokenServiceImpl.java | 44 +++-
.../security/tokens/TokenServiceIT.java | 42 +++
11 files changed, 449 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
[5/9] incubator-usergrid git commit: Merge branch 'master' into
USERGRID-567-central-sso
Posted by gr...@apache.org.
Merge branch 'master' into USERGRID-567-central-sso
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/f2525470
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/f2525470
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/f2525470
Branch: refs/heads/master
Commit: f25254709a16b8e8c0814a00b4484159a8c1f0a7
Parents: 9a8d5e0 6d962b7
Author: Dave Johnson <dm...@apigee.com>
Authored: Wed Apr 15 14:16:33 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Wed Apr 15 14:16:33 2015 -0400
----------------------------------------------------------------------
.../org/apache/usergrid/tools/ExportAdmins.java | 236 ++++++++++
.../org/apache/usergrid/tools/ImportAdmins.java | 451 +++++++++++++++++++
2 files changed, 687 insertions(+)
----------------------------------------------------------------------
[3/9] incubator-usergrid git commit: small refactor.
Posted by gr...@apache.org.
small refactor.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/27757b32
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/27757b32
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/27757b32
Branch: refs/heads/master
Commit: 27757b3292c97af81ca8e38a93a0c949811ee362
Parents: c15fcc8
Author: Dave Johnson <dm...@apigee.com>
Authored: Tue Apr 14 10:00:44 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Tue Apr 14 10:00:44 2015 -0400
----------------------------------------------------------------------
.../rest/management/ManagementResource.java | 75 +++++++++++++-------
1 file changed, 48 insertions(+), 27 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/27757b32/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 0dfd7a7..27dda53 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
@@ -32,6 +32,7 @@ import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
import org.apache.commons.lang.RandomStringUtils;
+import org.apache.usergrid.exception.NotImplementedException;
import org.apache.usergrid.management.ApplicationCreator;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.OrganizationOwnerInfo;
@@ -471,12 +472,15 @@ public class ManagementResource extends AbstractContextResource {
*/
@POST
@Path( "/externaltoken" )
- //@RequireSystemAccess TODO: should this be required
public Response validateExternalToken(
@Context UriInfo ui,
Map<String, Object> json,
@QueryParam( "callback" ) @DefaultValue( "" ) String callback ) throws Exception {
+ if ( StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ))) {
+ throw new NotImplementedException( "External Token Validation Service is not configured" );
+ }
+
Object extAccessTokenObj = json.get("ext_access_token");
if ( extAccessTokenObj == null ) {
throw new IllegalArgumentException("ext_access_token must be specified");
@@ -520,7 +524,6 @@ public class ManagementResource extends AbstractContextResource {
*/
@GET
@Path( "/externaltoken" )
- //@RequireSystemAccess TODO: should this be required
public Response validateExternalToken(
@Context UriInfo ui,
@QueryParam( "ext_access_token" ) String extAccessToken,
@@ -529,6 +532,10 @@ public class ManagementResource extends AbstractContextResource {
throws Exception {
+ if ( StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ))) {
+ throw new NotImplementedException( "External Token Validation Service is not configured" );
+ }
+
if ( extAccessToken == null ) {
throw new IllegalArgumentException("ext_access_token must be specified");
}
@@ -537,31 +544,9 @@ public class ManagementResource extends AbstractContextResource {
throw new IllegalArgumentException("ttl must be specified");
}
- // 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
-
- ClientConfig clientConfig = new DefaultClientConfig();
- clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
- Client client = Client.create(clientConfig);
- final JsonNode accessInfoNode;
- try {
- accessInfoNode = client.resource( me )
- .type(MediaType.APPLICATION_JSON_TYPE)
- .get(JsonNode.class);
-
- } catch ( Exception e ) {
- // user not found 404
- String msg = "Cannot find Admin User associated with " + extAccessToken;
- throw new EntityNotFoundException( msg, e );
- }
+ // look up user via UG Central's /management/me endpoint.
- // good. user was found in central Usergrid
+ JsonNode accessInfoNode = getMeFromUgCentral( extAccessToken );
JsonNode userNode = accessInfoNode.get( "user" );
String username = userNode.get( "username" ).getTextValue();
@@ -575,8 +560,8 @@ public class ManagementResource extends AbstractContextResource {
// if user does not exist locally then we need to fix that
final UUID userId;
-
final OrganizationInfo organizationInfo = management.getOrganizationByName(username);
+
if ( organizationInfo == null ) {
// create local user and personal organization, activate user.
@@ -605,6 +590,42 @@ public class ManagementResource extends AbstractContextResource {
}
+ /**
+ * 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 {
+
+ // 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
+
+ ClientConfig clientConfig = new DefaultClientConfig();
+ clientConfig.getFeatures().put( JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
+ Client client = Client.create(clientConfig);
+ final JsonNode accessInfoNode;
+ try {
+ accessInfoNode = client.resource( me )
+ .type( MediaType.APPLICATION_JSON_TYPE)
+ .get(JsonNode.class);
+
+ } catch ( Exception e ) {
+ // user not found 404
+ String msg = "Cannot find Admin User associated with " + extAccessToken;
+ throw new EntityNotFoundException( msg, e );
+ }
+ return accessInfoNode;
+ }
+
+
String errorMsg = "";
String responseType;
String clientId;
[6/9] incubator-usergrid git commit: Fixes from code review.
Posted by gr...@apache.org.
Fixes from code review.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/96ef88f6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/96ef88f6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/96ef88f6
Branch: refs/heads/master
Commit: 96ef88f691ae8b73dc7918c9adc96e4410790d40
Parents: f252547
Author: Dave Johnson <dm...@apigee.com>
Authored: Thu Apr 16 09:38:30 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Thu Apr 16 09:38:30 2015 -0400
----------------------------------------------------------------------
.../rest/management/ManagementResource.java | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/96ef88f6/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 1bae8eb..73963fe 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
@@ -201,9 +201,9 @@ public class ManagementResource extends AbstractContextResource {
String callback, boolean loadAdminData ) throws Exception {
- // if external tokens are enabled for Usegrid central authentication,
- // then only the superuser can login via this Usergrid instance.
- if ( externalTokensEnabled && !username.equalsIgnoreCase( superuserName )) {
+ // when external tokens (Usergrid Central SSO) are enabled only superuser can login as Admin User.
+ if ( externalTokensEnabled &&
+ superuserAllowed && !username.equalsIgnoreCase( superuserName )) {
// cause an HTTP 400 response with a useful message
throw new IllegalArgumentException("Admin Users must login via " +
@@ -583,17 +583,16 @@ public class ManagementResource extends AbstractContextResource {
// if user does not exist locally then we need to fix that
- UUID userId = null;
- final OrganizationInfo organizationInfo = management.getOrganizationByName(username);
+ UUID userId = management.getAdminUserByUsername( username ).getUuid();
- if ( organizationInfo == null ) {
+ if ( userId == null ) {
- // create local user and personal organization, activate user.
+ // create local user and and organizations they have on the central Usergrid instance
String name = userNode.get( "name" ).getTextValue();
String email = userNode.get( "email" ).getTextValue();
- // set dummy password to random string that nobody can guess, in SSO setup
+ // set dummy password to random string that nobody can guess, in central SSO setup
// admin users should never be able to login directly to this Usergrid system
String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 );
@@ -630,8 +629,6 @@ public class ManagementResource extends AbstractContextResource {
}
}
- } else {
- userId = management.getAdminUserByUsername( username ).getUuid();
}
// store the external access_token as if it were one of our own
[4/9] incubator-usergrid git commit: When provisioning a local user
for a valid external token,
create all organizations listed in the user's Access Info object. Also:
prevent Admin Users from logging in when external token validation is
enabled.
Posted by gr...@apache.org.
When provisioning a local user for a valid external token, create all organizations listed in the user's Access Info object. Also: prevent Admin Users from logging in when external token validation is enabled.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/9a8d5e07
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/9a8d5e07
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/9a8d5e07
Branch: refs/heads/master
Commit: 9a8d5e07822c5bd856f2aea2ec87232ad8888307
Parents: 27757b3
Author: Dave Johnson <dm...@apigee.com>
Authored: Wed Apr 15 11:24:52 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Wed Apr 15 11:24:52 2015 -0400
----------------------------------------------------------------------
.../main/resources/usergrid-default.properties | 5 ++
.../rest/management/ManagementResource.java | 89 ++++++++++++++++----
2 files changed, 78 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/9a8d5e07/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 cc4dda6..fe7a945 100644
--- a/stack/config/src/main/resources/usergrid-default.properties
+++ b/stack/config/src/main/resources/usergrid-default.properties
@@ -111,6 +111,11 @@ usergrid.sysadmin.login.allowed=false
usergrid.sysadmin.approve.users=false
usergrid.sysadmin.approve.organizations=false
+# Base URL of central Usergrid SSO server
+# Setting this will enable external token validation.
+# See also: https://issues.apache.org/jira/browse/USERGRID-567
+usergrid.central.url=
+
# Where to store temporary files
usergrid.temp.files=/tmp/usergrid
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/9a8d5e07/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 27dda53..1bae8eb 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
@@ -18,6 +18,7 @@ package org.apache.usergrid.rest.management;
import java.net.URLEncoder;
+import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
@@ -105,8 +106,25 @@ public class ManagementResource extends AbstractContextResource {
public static final String USERGRID_CENTRAL_URL = "usergrid.central.url";
+ private boolean superuserAllowed;
+
+ private boolean externalTokensEnabled;
+
+ private String superuserName;
+
public ManagementResource() {
+
+ String superuserAllowedStr = properties.getProperty( "usergrid.sysadmin.login.allowed" );
+
+ superuserName = properties.getProperty( "usergrid.sysadmin.login.name" );
+
+ superuserAllowed = !StringUtils.isEmpty( superuserAllowedStr )
+ && superuserAllowedStr.trim().equalsIgnoreCase( "true" );
+
+ externalTokensEnabled =
+ !StringUtils.isEmpty( properties.getProperty( USERGRID_CENTRAL_URL ));
+
logger.info( "ManagementResource initialized" );
}
@@ -181,6 +199,17 @@ public class ManagementResource extends AbstractContextResource {
private Response getAccessTokenInternal( UriInfo ui, String authorization, String grant_type, String username,
String password, String client_id, String client_secret, long ttl,
String callback, boolean loadAdminData ) throws Exception {
+
+
+ // if external tokens are enabled for Usegrid central authentication,
+ // then only the superuser can login via this Usergrid instance.
+ if ( externalTokensEnabled && !username.equalsIgnoreCase( superuserName )) {
+
+ // cause an HTTP 400 response with a useful message
+ throw new IllegalArgumentException("Admin Users must login via " +
+ properties.getProperty( USERGRID_CENTRAL_URL ));
+ }
+
UserInfo user = null;
try {
@@ -481,7 +510,7 @@ public class ManagementResource extends AbstractContextResource {
throw new NotImplementedException( "External Token Validation Service is not configured" );
}
- Object extAccessTokenObj = json.get("ext_access_token");
+ Object extAccessTokenObj = json.get( "ext_access_token" );
if ( extAccessTokenObj == null ) {
throw new IllegalArgumentException("ext_access_token must be specified");
}
@@ -505,9 +534,10 @@ public class ManagementResource extends AbstractContextResource {
/**
* <p>
* Validates access token from other or "external" Usergrid system.
- * Calls other system's /management/me endpoint to get the User associated with the access token.
- * If user does not exist locally, then user and organization with the same name of user is created.
- * If no user is returned from the other cluster, then this endpoint will return 401.
+ * Calls other system's /management/me endpoint to get the User
+ * associated with the access token. If user does not exist locally,
+ * then user and organizations will be created. If no user is returned
+ * from the other cluster, then this endpoint will return 401.
* </p>
*
* <p> Part of Usergrid Central SSO feature.
@@ -550,29 +580,55 @@ public class ManagementResource extends AbstractContextResource {
JsonNode userNode = accessInfoNode.get( "user" );
String username = userNode.get( "username" ).getTextValue();
- String name = userNode.get( "name" ).getTextValue();
- String email = userNode.get( "email" ).getTextValue();
-
- // set dummy password to random string that nobody can guess, in SSO setup
- // admin users should never be able to login directly to this Usergrid system
- String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 );
// if user does not exist locally then we need to fix that
- final UUID userId;
+ UUID userId = null;
final OrganizationInfo organizationInfo = management.getOrganizationByName(username);
if ( organizationInfo == null ) {
// create local user and personal organization, activate user.
- OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization(
- username, username, name, email, dummyPassword, true, true );
- userId = ownerOrgInfo.getOwner().getUuid();
+ String name = userNode.get( "name" ).getTextValue();
+ String email = userNode.get( "email" ).getTextValue();
+
+ // set dummy password to random string that nobody can guess, in SSO setup
+ // admin users should never be able to login directly to this Usergrid system
+ String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 );
+
+ JsonNode orgsNode = userNode.get( "organizations" );
+ final Iterator<String> fieldNames = orgsNode.getFieldNames();
+
+ UserInfo userInfo = null;
+
+ // create user and any organizations that user is supposed to have
+
+ while ( fieldNames.hasNext() ) {
+
+ String orgName = fieldNames.next();
+
+ if ( userId == null ) {
- management.activateOrganization( ownerOrgInfo.getOrganization() );
+ // haven't created user yet so do that now
+ OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization(
+ orgName, username, name, email, dummyPassword, true, true );
- applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() );
+ management.activateOrganization( ownerOrgInfo.getOrganization() ); // redundant?
+ applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() );
+
+ userId = ownerOrgInfo.getOwner().getUuid();
+ userInfo = ownerOrgInfo.getOwner();
+
+ } else {
+
+ // already created user, so just create an org
+ final OrganizationInfo organization = management.createOrganization( orgName, userInfo, true );
+
+ management.activateOrganization( organization ); // redundant?
+ applicationCreator.createSampleFor( organization );
+ }
+ }
} else {
userId = management.getAdminUserByUsername( username ).getUuid();
@@ -602,6 +658,7 @@ public class ManagementResource extends AbstractContextResource {
// 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;
[2/9] incubator-usergrid git commit: Fixes to Usergrid Central SSO
feature from manual testing.
Posted by gr...@apache.org.
Fixes to Usergrid Central SSO feature from manual testing.
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/c15fcc8c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/c15fcc8c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/c15fcc8c
Branch: refs/heads/master
Commit: c15fcc8cd04637c2cb45e390035520751295b77d
Parents: 098355f
Author: Dave Johnson <dm...@apigee.com>
Authored: Mon Apr 13 16:47:39 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Mon Apr 13 16:47:39 2015 -0400
----------------------------------------------------------------------
.../rest/management/ManagementResource.java | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/c15fcc8c/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 c1c0be5..0dfd7a7 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
@@ -32,14 +32,14 @@ import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;
import org.apache.commons.lang.RandomStringUtils;
+import org.apache.usergrid.management.ApplicationCreator;
import org.apache.usergrid.management.OrganizationInfo;
import org.apache.usergrid.management.OrganizationOwnerInfo;
import org.apache.usergrid.persistence.exceptions.EntityNotFoundException;
-import org.apache.usergrid.rest.security.annotations.RequireSystemAccess;
-import org.apache.usergrid.utils.UUIDUtils;
import org.codehaus.jackson.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -99,6 +99,9 @@ public class ManagementResource extends AbstractContextResource {
private static final Logger logger = LoggerFactory.getLogger( ManagementResource.class );
+ @Autowired
+ private ApplicationCreator applicationCreator;
+
public static final String USERGRID_CENTRAL_URL = "usergrid.central.url";
@@ -561,7 +564,6 @@ public class ManagementResource extends AbstractContextResource {
// good. user was found in central Usergrid
JsonNode userNode = accessInfoNode.get( "user" );
- UUID userId = UUIDUtils.tryExtractUUID( userNode.get( "uuid" ).getTextValue() );
String username = userNode.get( "username" ).getTextValue();
String name = userNode.get( "name" ).getTextValue();
String email = userNode.get( "email" ).getTextValue();
@@ -572,13 +574,23 @@ public class ManagementResource extends AbstractContextResource {
// if user does not exist locally then we need to fix that
+ final UUID userId;
+
final OrganizationInfo organizationInfo = management.getOrganizationByName(username);
if ( organizationInfo == null ) {
// create local user and personal organization, activate user.
OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization(
- username, username, name, email, dummyPassword, true, true);
+ username, username, name, email, dummyPassword, true, true );
+ userId = ownerOrgInfo.getOwner().getUuid();
+
+ management.activateOrganization( ownerOrgInfo.getOrganization() );
+
+ applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() );
+
+ } else {
+ userId = management.getAdminUserByUsername( username ).getUuid();
}
// store the external access_token as if it were one of our own
[8/9] incubator-usergrid git commit: Merge branch
'USERGRID-567-central-sso'
Posted by gr...@apache.org.
Merge branch 'USERGRID-567-central-sso'
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/6ae492e3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/6ae492e3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/6ae492e3
Branch: refs/heads/master
Commit: 6ae492e37d9142e73469da250cf5d976eed17bbe
Parents: 7aabf68 96ef88f
Author: Dave Johnson <dm...@apigee.com>
Authored: Thu Apr 16 09:41:29 2015 -0400
Committer: Dave Johnson <dm...@apigee.com>
Committed: Thu Apr 16 09:41:29 2015 -0400
----------------------------------------------------------------------
.../rest/management/ManagementResource.java | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
----------------------------------------------------------------------