| /* |
| * 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 com.codahale.metrics.Counter; |
| import com.codahale.metrics.Timer; |
| 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 com.sun.jersey.api.view.Viewable; |
| import com.sun.jersey.client.apache.ApacheHttpClient; |
| import com.sun.jersey.client.apache.ApacheHttpClientHandler; |
| import org.apache.amber.oauth2.common.error.OAuthError; |
| import org.apache.amber.oauth2.common.exception.OAuthProblemException; |
| import org.apache.amber.oauth2.common.message.OAuthResponse; |
| import org.apache.amber.oauth2.common.message.types.GrantType; |
| import org.apache.commons.httpclient.HttpClient; |
| import org.apache.commons.httpclient.HttpConnectionManager; |
| import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; |
| import org.apache.commons.httpclient.params.HttpClientParams; |
| import org.apache.commons.httpclient.params.HttpConnectionManagerParams; |
| import org.apache.commons.httpclient.params.HttpMethodParams; |
| import org.apache.commons.lang.RandomStringUtils; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; |
| import org.apache.shiro.codec.Base64; |
| 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.management.UserInfo; |
| import org.apache.usergrid.management.exceptions.DisabledAdminUserException; |
| import org.apache.usergrid.management.exceptions.UnactivatedAdminUserException; |
| import org.apache.usergrid.management.exceptions.UnconfirmedAdminUserException; |
| import org.apache.usergrid.metrics.MetricsFactory; |
| import org.apache.usergrid.persistence.exceptions.EntityNotFoundException; |
| import org.apache.usergrid.rest.AbstractContextResource; |
| import org.apache.usergrid.rest.exceptions.RedirectionException; |
| import org.apache.usergrid.rest.management.organizations.OrganizationsResource; |
| import org.apache.usergrid.rest.management.users.UsersResource; |
| import org.apache.usergrid.security.oauth.AccessInfo; |
| import org.apache.usergrid.security.shiro.utils.SubjectUtils; |
| 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; |
| |
| 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 java.net.URLEncoder; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| import static javax.servlet.http.HttpServletResponse.*; |
| import static javax.ws.rs.core.MediaType.*; |
| import static org.apache.commons.lang.StringUtils.isNotBlank; |
| import static org.apache.usergrid.utils.JsonUtils.mapToJsonString; |
| import static org.apache.usergrid.utils.StringUtils.stringOrSubstringAfterFirst; |
| import static org.apache.usergrid.utils.StringUtils.stringOrSubstringBeforeFirst; |
| |
| |
| @Path( "/management" ) |
| @Component |
| @Scope( "singleton" ) |
| @Produces( { |
| MediaType.APPLICATION_JSON, "application/javascript", "application/x-javascript", "text/ecmascript", |
| "application/ecmascript", "text/jscript" |
| } ) |
| public class ManagementResource extends AbstractContextResource { |
| |
| private static final Logger logger = LoggerFactory.getLogger( ManagementResource.class ); |
| |
| /*- |
| * New endpoints: |
| * |
| * /management/externaltoken?ext_access_token=<token-from-central-usergrid>&ttl=<time-to-live> |
| * |
| * /management/organizations/<organization-name>/applications |
| * /management/organizations/<organization-name>/users |
| * /management/organizations/<organization-name>/keys |
| * |
| * /management/users/<user-name>/login |
| * /management/users/<user-name>/password |
| * |
| */ |
| |
| @Autowired |
| private ApplicationCreator applicationCreator; |
| |
| @Autowired |
| MetricsFactory metricsFactory; |
| |
| private static Client jerseyClient = null; |
| |
| |
| // names for metrics to be collected |
| private static final String SSO_TOKENS_REJECTED = "sso_tokens_rejected"; |
| private static final String SSO_TOKENS_VALIDATED = "sso_tokens_validated"; |
| private static final String SSO_CREATED_LOCAL_ADMINS = "sso_created_local_admins"; |
| private static final String SSO_PROCESSING_TIME = "sso_processing_time"; |
| |
| // usergrid configuration property names needed |
| public static final String USERGRID_SYSADMIN_LOGIN_NAME = "usergrid.sysadmin.login.name"; |
| public static final String USERGRID_CENTRAL_URL = "usergrid.central.url"; |
| public static final String CENTRAL_CONNECTION_POOL_SIZE = "usergrid.central.connection.pool.size"; |
| public static final String CENTRAL_CONNECTION_TIMEOUT = "usergrid.central.connection.timeout"; |
| public static final String CENTRAL_READ_TIMEOUT = "usergrid.central.read.timeout"; |
| |
| public ManagementResource() { |
| logger.info( "ManagementResource initialized" ); |
| } |
| |
| |
| private static String wrapWithCallback( AccessInfo accessInfo, String callback ) { |
| return wrapWithCallback( mapToJsonString( accessInfo ), callback ); |
| } |
| |
| |
| private static String wrapWithCallback( String json, String callback ) { |
| if ( StringUtils.isNotBlank( callback ) ) { |
| json = callback + "(" + json + ")"; |
| } |
| return json; |
| } |
| |
| |
| private static MediaType jsonMediaType( String callback ) { |
| return isNotBlank( callback ) ? new MediaType( "application", "javascript" ) : APPLICATION_JSON_TYPE; |
| } |
| |
| |
| @Path( "organizations" ) |
| public OrganizationsResource getOrganizations() { |
| return getSubResource( OrganizationsResource.class ); |
| } |
| |
| |
| @Path( "orgs" ) |
| public OrganizationsResource getOrganizations2() { |
| return getSubResource( OrganizationsResource.class ); |
| } |
| |
| |
| @Path( "users" ) |
| public UsersResource getUsers() { |
| return getSubResource( UsersResource.class ); |
| } |
| |
| |
| @GET |
| @Path( "me" ) |
| public Response getAccessTokenLight( @Context UriInfo ui, @HeaderParam( "Authorization" ) String authorization, |
| @QueryParam( "grant_type" ) String grant_type, |
| @QueryParam( "username" ) String username, |
| @QueryParam( "password" ) String password, |
| @QueryParam( "client_id" ) String client_id, |
| @QueryParam( "client_secret" ) String client_secret, |
| @QueryParam( "ttl" ) long ttl, |
| @QueryParam( "access_token" ) String access_token, |
| @QueryParam( "callback" ) @DefaultValue( "" ) String callback ) |
| throws Exception { |
| return getAccessTokenInternal( ui, authorization, grant_type, username, password, client_id, client_secret, ttl, |
| callback, false, true ); |
| } |
| |
| |
| @GET |
| @Path( "token" ) |
| public Response getAccessToken( @Context UriInfo ui, @HeaderParam( "Authorization" ) String authorization, |
| @QueryParam( "grant_type" ) String grant_type, |
| @QueryParam( "username" ) String username, |
| @QueryParam( "password" ) String password, |
| @QueryParam( "client_id" ) String client_id, |
| @QueryParam( "client_secret" ) String client_secret, @QueryParam( "ttl" ) long ttl, |
| @QueryParam( "callback" ) @DefaultValue( "" ) String callback ) throws Exception { |
| return getAccessTokenInternal( ui, authorization, grant_type, username, password, client_id, client_secret, ttl, |
| callback, false, false); |
| } |
| |
| |
| 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 adminData, boolean me) throws Exception { |
| |
| |
| UserInfo user = null; |
| |
| try { |
| if ( SubjectUtils.getUser() != null ) { |
| user = SubjectUtils.getUser(); |
| } |
| |
| logger.info( "ManagementResource.getAccessToken with username: {}", username ); |
| |
| String errorDescription = "invalid username or password"; |
| |
| if ( user == null ) { |
| |
| if ( !me ) { // if not lightweight-auth, i.e. /management/me then... |
| |
| // 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(); |
| |
| if ( "BASIC".equals( type ) ) { |
| String token = stringOrSubstringAfterFirst( authorization, ' ' ); |
| String[] values = Base64.decodeToString( token ).split( ":" ); |
| |
| if ( values.length >= 2 ) { |
| client_id = values[0].toLowerCase(); |
| client_secret = values[1]; |
| } |
| } |
| } |
| |
| |
| // do checking for different grant types |
| if ( GrantType.PASSWORD.toString().equals( grant_type ) ) { |
| try { |
| user = management.verifyAdminUserPasswordCredentials( username, password ); |
| |
| if ( user != null ) { |
| logger.info( "found user from verify: {}", user.getUuid() ); |
| } |
| } |
| catch ( UnactivatedAdminUserException uaue ) { |
| errorDescription = "user not activated"; |
| logger.error( errorDescription, uaue ); |
| } |
| catch ( DisabledAdminUserException daue ) { |
| errorDescription = "user disabled"; |
| logger.error( errorDescription, daue ); |
| } |
| catch ( UnconfirmedAdminUserException uaue ) { |
| errorDescription = "User must be confirmed to authenticate"; |
| logger.warn( "Responding with HTTP 403 forbidden response for unconfirmed user {}" , user); |
| |
| OAuthResponse response = OAuthResponse.errorResponse( SC_FORBIDDEN ) |
| .setError( OAuthError.TokenResponse.INVALID_GRANT ) |
| .setErrorDescription( errorDescription ) |
| .buildJSONMessage(); |
| |
| return Response.status( response.getResponseStatus() ).type( jsonMediaType( callback ) ) |
| .entity( wrapWithCallback( response.getBody(), callback ) ).build(); |
| } |
| catch ( Exception e1 ) { |
| logger.error( errorDescription, e1 ); |
| } |
| } |
| else if ( "client_credentials".equals( grant_type ) ) { |
| try { |
| AccessInfo access_info = management.authorizeClient( client_id, client_secret, ttl ); |
| if ( access_info != null ) { |
| |
| return Response.status( SC_OK ).type( jsonMediaType( callback ) ) |
| .entity( wrapWithCallback( access_info, callback ) ).build(); |
| } |
| } |
| catch ( Exception e1 ) { |
| logger.error( "failed authorizeClient", e1 ); |
| } |
| } |
| } |
| |
| if ( user == null ) { |
| OAuthResponse response = |
| OAuthResponse.errorResponse( SC_BAD_REQUEST ).setError( OAuthError.TokenResponse.INVALID_GRANT ) |
| .setErrorDescription( errorDescription ).buildJSONMessage(); |
| return Response.status( response.getResponseStatus() ).type( jsonMediaType( callback ) ) |
| .entity( wrapWithCallback( response.getBody(), callback ) ).build(); |
| } |
| |
| String token = management.getAccessTokenForAdminUser( user.getUuid(), ttl ); |
| Long passwordChanged = management.getLastAdminPasswordChange( user.getUuid() ); |
| |
| AccessInfo access_info = |
| new AccessInfo().withExpiresIn( tokens.getMaxTokenAgeInSeconds( token ) ).withAccessToken( token ) |
| .withPasswordChanged( passwordChanged ); |
| |
| access_info.setProperty( "user", management.getAdminUserOrganizationData( user, me ) ); |
| |
| // increment counters for admin login |
| management.countAdminUserAction( user, "login" ); |
| |
| return Response.status( SC_OK ).type( jsonMediaType( callback ) ) |
| .entity( wrapWithCallback( access_info, callback ) ).build(); |
| } |
| catch ( OAuthProblemException e ) { |
| logger.error( "OAuth Error", e ); |
| OAuthResponse res = OAuthResponse.errorResponse( SC_BAD_REQUEST ).error( e ).buildJSONMessage(); |
| return Response.status( res.getResponseStatus() ).type( jsonMediaType( callback ) ) |
| .entity( wrapWithCallback( res.getBody(), callback ) ).build(); |
| } |
| } |
| |
| |
| @POST |
| @Path( "token" ) |
| @Consumes( APPLICATION_FORM_URLENCODED ) |
| public Response getAccessTokenPost( @Context UriInfo ui, @FormParam( "grant_type" ) String grant_type, |
| @HeaderParam( "Authorization" ) String authorization, |
| @FormParam( "username" ) String username, |
| @FormParam( "password" ) String password, |
| @FormParam( "client_id" ) String client_id, @FormParam( "ttl" ) long ttl, |
| @FormParam( "client_secret" ) String client_secret, |
| @QueryParam( "callback" ) @DefaultValue( "" ) String callback ) |
| throws Exception { |
| |
| logger.info( "ManagementResource.getAccessTokenPost" ); |
| |
| return getAccessTokenInternal( ui, authorization, grant_type, username, password, client_id, client_secret, ttl, |
| callback, false, false); |
| } |
| |
| |
| @POST |
| @Path( "me" ) |
| @Consumes( APPLICATION_FORM_URLENCODED ) |
| public Response getAccessTokenLightPost( @Context UriInfo ui, @HeaderParam( "Authorization" ) String authorization, |
| @FormParam( "grant_type" ) String grant_type, |
| @FormParam( "username" ) String username, |
| @FormParam( "password" ) String password, |
| @FormParam( "client_id" ) String client_id, |
| @FormParam( "client_secret" ) String client_secret, |
| @FormParam( "ttl" ) long ttl, |
| @FormParam( "access_token" ) String access_token, |
| @FormParam( "callback" ) @DefaultValue( "" ) String callback ) |
| throws Exception { |
| return getAccessTokenInternal( ui, authorization, grant_type, username, password, client_id, client_secret, ttl, |
| callback, false, true ); |
| } |
| |
| |
| @POST |
| @Path( "token" ) |
| @Consumes( APPLICATION_JSON ) |
| public Response getAccessTokenPostJson( @Context UriInfo ui, @HeaderParam( "Authorization" ) String authorization, |
| Map<String, Object> json, |
| @QueryParam( "callback" ) @DefaultValue( "" ) String callback ) |
| throws Exception { |
| |
| String grant_type = ( String ) json.get( "grant_type" ); |
| String username = ( String ) json.get( "username" ); |
| String password = ( String ) json.get( "password" ); |
| String client_id = ( String ) json.get( "client_id" ); |
| String client_secret = ( String ) json.get( "client_secret" ); |
| long ttl = 0; |
| |
| if ( json.get( "ttl" ) != null ) { |
| try { |
| ttl = Long.parseLong( json.get( "ttl" ).toString() ); |
| } |
| catch ( NumberFormatException nfe ) { |
| throw new IllegalArgumentException( "ttl must be a number >= 0" ); |
| } |
| } |
| |
| return getAccessTokenInternal( ui, authorization, grant_type, username, password, client_id, client_secret, ttl, |
| callback, false, false ); |
| } |
| |
| |
| @POST |
| @Path( "me" ) |
| @Consumes( APPLICATION_JSON ) |
| public Response getAccessTokenMePostJson( @Context UriInfo ui, Map<String, Object> json, |
| @QueryParam( "callback" ) @DefaultValue( "" ) String callback, |
| @HeaderParam( "Authorization" ) String authorization ) throws Exception { |
| |
| String grant_type = ( String ) json.get( "grant_type" ); |
| String username = ( String ) json.get( "username" ); |
| String password = ( String ) json.get( "password" ); |
| String client_id = ( String ) json.get( "client_id" ); |
| String client_secret = ( String ) json.get( "client_secret" ); |
| long ttl = 0; |
| |
| if ( json.get( "ttl" ) != null ) { |
| try { |
| ttl = Long.parseLong( json.get( "ttl" ).toString() ); |
| } |
| catch ( NumberFormatException nfe ) { |
| throw new IllegalArgumentException( "ttl must be a number >= 0" ); |
| } |
| } |
| |
| return getAccessTokenInternal( ui, authorization, grant_type, username, password, client_id, client_secret, ttl, |
| callback, false, false ); |
| } |
| |
| |
| @GET |
| @Path( "authorize" ) |
| @Produces( MediaType.TEXT_HTML ) |
| public Viewable showAuthorizeForm( @Context UriInfo ui, @QueryParam( "response_type" ) String response_type, |
| @QueryParam( "client_id" ) String client_id, |
| @QueryParam( "redirect_uri" ) String redirect_uri, |
| @QueryParam( "scope" ) String scope, @QueryParam( "state" ) String state ) { |
| |
| responseType = response_type; |
| clientId = client_id; |
| redirectUri = redirect_uri; |
| this.scope = scope; |
| this.state = state; |
| |
| return handleViewable( "authorize_form", this ); |
| } |
| |
| |
| @POST |
| @Path( "authorize" ) |
| @Produces( MediaType.TEXT_HTML ) |
| public Viewable handleAuthorizeForm( @Context UriInfo ui, @FormParam( "response_type" ) String response_type, |
| @FormParam( "client_id" ) String client_id, |
| @FormParam( "redirect_uri" ) String redirect_uri, |
| @FormParam( "scope" ) String scope, @FormParam( "state" ) String state, |
| @FormParam( "username" ) String username, |
| @FormParam( "password" ) String password ) { |
| |
| try { |
| responseType = response_type; |
| clientId = client_id; |
| redirectUri = redirect_uri; |
| this.scope = scope; |
| this.state = state; |
| |
| UserInfo user = null; |
| try { |
| user = management.verifyAdminUserPasswordCredentials( username, password ); |
| } |
| catch ( Exception e1 ) { |
| } |
| if ( ( user != null ) && isNotBlank( redirect_uri ) ) { |
| if ( !redirect_uri.contains( "?" ) ) { |
| redirect_uri += "?"; |
| } |
| else { |
| redirect_uri += "&"; |
| } |
| redirect_uri += "code=" + management.getAccessTokenForAdminUser( user.getUuid(), 0 ); |
| if ( isNotBlank( state ) ) { |
| redirect_uri += "&state=" + URLEncoder.encode( state, "UTF-8" ); |
| } |
| throw new RedirectionException( state ); |
| } |
| else { |
| errorMsg = "Username or password do not match"; |
| } |
| |
| return handleViewable( "authorize_form", this ); |
| } |
| catch ( RedirectionException e ) { |
| throw e; |
| } |
| catch ( Exception e ) { |
| return handleViewable( "error", e ); |
| } |
| } |
| |
| |
| /** |
| * <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; |
| |
| Timer processingTimer = metricsFactory.getTimer( |
| ManagementResource.class, SSO_PROCESSING_TIME ); |
| |
| Timer.Context timerContext = processingTimer.time(); |
| |
| 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 |
| logger.info("User {} does not exist locally, creating", username ); |
| |
| String name = userNode.get( "name" ).getTextValue(); |
| String email = userNode.get( "email" ).getTextValue(); |
| String dummyPassword = RandomStringUtils.randomAlphanumeric( 40 ); |
| |
| JsonNode orgsNode = userNode.get( "organizations" ); |
| Iterator<String> fieldNames = orgsNode.getFieldNames(); |
| |
| if ( !fieldNames.hasNext() ) { |
| // no organizations for user exist in response from central Usergrid SSO |
| // so create user's personal organization and use username as organization name |
| fieldNames = Collections.singletonList( username ).iterator(); |
| } |
| |
| // 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, false ); |
| |
| applicationCreator.createSampleFor( ownerOrgInfo.getOrganization() ); |
| |
| userId = ownerOrgInfo.getOwner().getUuid(); |
| userInfo = ownerOrgInfo.getOwner(); |
| |
| Counter createdAdminsCounter = metricsFactory.getCounter( |
| ManagementResource.class, SSO_CREATED_LOCAL_ADMINS ); |
| createdAdminsCounter.inc(); |
| |
| logger.info( "Created user {} and org {}", username, orgName ); |
| |
| } else { |
| |
| // already created user, so just create an org |
| final OrganizationInfo organization = management.createOrganization( orgName, userInfo, true ); |
| |
| applicationCreator.createSampleFor( organization ); |
| |
| logger.info( "Created user {}'s other org {}", username, orgName ); |
| } |
| } |
| |
| } |
| |
| // 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) { |
| timerContext.stop(); |
| logger.debug("Error validating external token", e); |
| throw e; |
| } |
| |
| final Response response = Response.status( SC_OK ).type( jsonMediaType( callback ) ).entity( accessInfo ).build(); |
| |
| timerContext.stop(); |
| |
| return response; |
| } |
| |
| /** |
| * 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 { |
| |
| // prepare to count tokens validated and rejected |
| |
| Counter tokensRejectedCounter = metricsFactory.getCounter( |
| ManagementResource.class, SSO_TOKENS_REJECTED ); |
| Counter tokensValidatedCounter = metricsFactory.getCounter( |
| ManagementResource.class, SSO_TOKENS_VALIDATED ); |
| |
| // 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 |
| |
| Client client = getJerseyClient(); |
| final JsonNode accessInfoNode; |
| try { |
| accessInfoNode = client.resource( me ) |
| .type( MediaType.APPLICATION_JSON_TYPE) |
| .get(JsonNode.class); |
| |
| tokensValidatedCounter.inc(); |
| |
| } catch ( Exception e ) { |
| // user not found 404 |
| tokensRejectedCounter.inc(); |
| String msg = "Cannot find Admin User associated with " + extAccessToken; |
| throw new EntityNotFoundException( msg, e ); |
| } |
| |
| return accessInfoNode; |
| } |
| |
| |
| synchronized Client getJerseyClient() { |
| |
| if ( jerseyClient == null ) { |
| |
| // create HTTPClient and with configured connection pool |
| |
| int poolSize = 100; // connections |
| final String poolSizeStr = properties.getProperty( CENTRAL_CONNECTION_POOL_SIZE ); |
| if ( poolSizeStr != null ) { |
| poolSize = Integer.parseInt( poolSizeStr ); |
| } |
| |
| MultiThreadedHttpConnectionManager cm = new MultiThreadedHttpConnectionManager(); |
| HttpConnectionManagerParams cmParams = cm.getParams(); |
| cmParams.setMaxTotalConnections( poolSize ); |
| HttpClient httpClient = new HttpClient( cm ); |
| |
| // create Jersey Client using that HTTPClient and with configured timeouts |
| |
| int timeout = 20000; // ms |
| final String timeoutStr = properties.getProperty( CENTRAL_CONNECTION_TIMEOUT ); |
| if ( timeoutStr != null ) { |
| timeout = Integer.parseInt( timeoutStr ); |
| } |
| |
| int readTimeout = 20000; // ms |
| final String readTimeoutStr = properties.getProperty( CENTRAL_READ_TIMEOUT ); |
| if ( readTimeoutStr != null ) { |
| readTimeout = Integer.parseInt( readTimeoutStr ); |
| } |
| |
| ClientConfig clientConfig = new DefaultClientConfig(); |
| clientConfig.getFeatures().put( JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE ); |
| clientConfig.getProperties().put( ClientConfig.PROPERTY_CONNECT_TIMEOUT, timeout ); // ms |
| clientConfig.getProperties().put( ClientConfig.PROPERTY_READ_TIMEOUT, readTimeout ); // ms |
| |
| ApacheHttpClientHandler handler = new ApacheHttpClientHandler( httpClient, clientConfig ); |
| jerseyClient = new ApacheHttpClient( handler ); |
| } |
| |
| return jerseyClient; |
| } |
| |
| |
| /** |
| * 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 ) { |
| |
| if ( username == null || grant_type == null || !grant_type.equalsIgnoreCase( "password" )) { |
| return; // we only care about username/password auth |
| } |
| |
| 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 ); |
| if ( !username.equalsIgnoreCase( superuserName )) { |
| |
| // 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; |
| String redirectUri; |
| String scope; |
| String state; |
| |
| |
| public String getErrorMsg() { |
| return errorMsg; |
| } |
| |
| |
| public String getResponseType() { |
| return responseType; |
| } |
| |
| |
| public String getClientId() { |
| return clientId; |
| } |
| |
| |
| public String getRedirectUri() { |
| return redirectUri; |
| } |
| |
| |
| public String getScope() { |
| return scope; |
| } |
| |
| |
| public String getState() { |
| return state; |
| } |
| } |