[ https://issues.apache.org/jira/browse/USERGRID-567?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14498192#comment-14498192 ]
ASF GitHub Bot commented on USERGRID-567: ----------------------------------------- Github user snoopdave commented on a diff in the pull request: https://github.com/apache/incubator-usergrid/pull/224#discussion_r28521613 --- Diff: stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java --- @@ -441,6 +464,229 @@ public Viewable handleAuthorizeForm( @Context UriInfo ui, @FormParam( "response_ } + /** + * <p> + * Allows call to validateExternalToken() (see below) with a POST of a JSON object. + * </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" ) + 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"); + } + 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 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. + * 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" ) + 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 ( 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"); + } + + if ( ttl == -1 ) { + throw new IllegalArgumentException("ttl must be specified"); + } + AccessInfo accessInfo = null; + + try { + // look up user via UG Central's /management/me endpoint. + + JsonNode accessInfoNode = getMeFromUgCentral( extAccessToken ); + + JsonNode userNode = accessInfoNode.get( "user" ); + String username = userNode.get( "username" ).getTextValue(); + + // if user does not exist locally then we need to fix that + + UserInfo userInfo = management.getAdminUserByUsername( username ); + UUID userId = userInfo == null ? null : userInfo.getUuid(); + + if ( userId == null ) { + + // create local user and and organizations they have on the central Usergrid instance + + String name = userNode.get( "name" ).getTextValue(); + String email = userNode.get( "email" ).getTextValue(); + String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 ); + + JsonNode orgsNode = userNode.get( "organizations" ); + final Iterator<String> fieldNames = orgsNode.getFieldNames(); + + // create user and any organizations that user is supposed to have + + while ( fieldNames.hasNext() ) { + + String orgName = fieldNames.next(); + + if ( userId == null ) { + + // haven't created user yet so do that now + OrganizationOwnerInfo ownerOrgInfo = management.createOwnerAndOrganization( + orgName, username, name, email, dummyPassword, true, true ); + + 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 ); + } + } + + } + + // 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 ); + + } catch (Exception e) { + logger.debug("Error validating external token", e); + throw e; + } + + return Response.status( SC_OK ).type( jsonMediaType( callback ) ).entity( accessInfo ).build(); + } + + + /** + * 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; + } + + + /** --- End diff -- I moved the "superuser-only login when external token validation enabled" logic in the below new method: > Validate External Token for Usergrid Central SSO > ------------------------------------------------ > > Key: USERGRID-567 > URL: https://issues.apache.org/jira/browse/USERGRID-567 > Project: Usergrid > Issue Type: Story > Reporter: David Johnson > Assignee: David Johnson > > The requirement is to have multiple Usergrid systems, each with its own > Cassandra cluster, be able to authenticate Admin Users with one central > Usergrid system -- giving Admin Users Single-Sign-On (SSO) across all of > those systems. > We can do this by adding just one new end-point to Usergrid. > This Google Doc explains a complete design for "Usergrid Central SSO": > https://docs.google.com/document/d/12kXgaYcB6L9JoTyRGn0ZHEMg3vL1LJDqvtnltIBDa1Y/edit?usp=sharing > The design is based on earlier work by Ed Anuff and Nate McCall. -- This message was sent by Atlassian JIRA (v6.3.4#6332)