[ https://issues.apache.org/jira/browse/USERGRID-567?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14494566#comment-14494566 ]
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/221#discussion_r28356858 --- Diff: stack/rest/src/main/java/org/apache/usergrid/rest/management/ManagementResource.java --- @@ -441,6 +451,181 @@ public Viewable handleAuthorizeForm( @Context UriInfo ui, @FormParam( "response_ } + /** + * <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" ) + 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 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" ) + 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"); + } + + // 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(); + 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; + 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(); + + 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 + 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(); + } + + + /** + * 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 ) { --- End diff -- can we expose more specific error information, and throw different exceptions, here based on what comes back from the request? > 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)