/* * JBoss, Home of Professional Open Source * * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed 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.picketlink.oauth.server.util; import java.io.IOException; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.PropertyNamingStrategy; import org.jboss.logging.Logger; import org.picketlink.idm.IdentityManager; import org.picketlink.idm.config.IdentityConfigurationBuilder; import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.UsernamePasswordCredentials; import org.picketlink.idm.internal.DefaultPartitionManager; import org.picketlink.idm.jpa.internal.JPAIdentityStore; import org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity; import org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity; import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.AttributedType; import org.picketlink.idm.model.basic.Agent; import org.picketlink.idm.model.basic.Realm; import org.picketlink.idm.query.IdentityQuery; import org.picketlink.idm.spi.ContextInitializer; import org.picketlink.idm.spi.IdentityContext; import org.picketlink.idm.spi.IdentityStore; import org.picketlink.oauth.common.OAuthConstants; import org.picketlink.oauth.grants.AuthorizationCodeGrant; import org.picketlink.oauth.grants.ResourceOwnerPasswordCredentialsGrant; import org.picketlink.oauth.grants.ResourceOwnerPasswordCredentialsGrant.PasswordAccessTokenRequest; import org.picketlink.oauth.messages.AccessTokenRequest; import org.picketlink.oauth.messages.AuthorizationRequest; import org.picketlink.oauth.messages.ErrorResponse; import org.picketlink.oauth.messages.ErrorResponse.ErrorResponseCode; import org.picketlink.oauth.messages.OAuthResponse; import org.picketlink.oauth.messages.RegistrationRequest; import org.picketlink.oauth.messages.ResourceAccessRequest; /** * Utility * * @author anil saldhana * @since Dec 12, 2012 */ public class OAuthServerUtil { private static Logger log = Logger.getLogger(OAuthServerUtil.class); private static EntityManagerFactory entityManagerFactory; private static ThreadLocal<EntityManager> entityManagerThreadLocal = new ThreadLocal<EntityManager>(); /** * Centralize the IDM setup * * @param context * @return * @throws IOException */ public static IdentityManager handleIdentityManager(ServletContext context) throws IOException { IdentityManager identityManager = null; if (context == null) { throw new IllegalArgumentException("context is null"); } identityManager = (IdentityManager) context.getAttribute("identityManager"); if (identityManager == null) { entityManagerFactory = Persistence.createEntityManagerFactory("picketlink-oauth-pu"); final EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); entityManagerThreadLocal.set(entityManager); IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); builder .named("oauth") .stores() .jpa().mappedEntity( AccountTypeEntity.class, RoleTypeEntity.class, GroupTypeEntity.class, IdentityTypeEntity.class, RelationshipTypeEntity.class, RelationshipIdentityTypeEntity.class, PartitionTypeEntity.class, PasswordCredentialTypeEntity.class, DigestCredentialTypeEntity.class, X509CredentialTypeEntity.class, OTPCredentialTypeEntity.class, AttributeTypeEntity.class ).addContextInitializer(new ContextInitializer() { @Override public void initContextForStore(IdentityContext ctx, IdentityStore<?> store) { if (store instanceof JPAIdentityStore) { if (!ctx.isParameterSet(JPAIdentityStore.INVOCATION_CTX_ENTITY_MANAGER)) { ctx.setParameter(JPAIdentityStore.INVOCATION_CTX_ENTITY_MANAGER, entityManager); } } } }).supportAllFeatures(); DefaultPartitionManager partitionManager = new DefaultPartitionManager(builder.buildAll()); if (partitionManager.getPartition(Realm.class, Realm.DEFAULT_REALM) == null) { partitionManager.add(new Realm(Realm.DEFAULT_REALM)); } // FIXME: IdentityManager is not threadsafe identityManager = partitionManager.createIdentityManager(); context.setAttribute("identityManager", identityManager); } return identityManager; } /** * Handle an Authorization Code Grant Type Request * * @param request * @param identityManager * @return */ public static OAuthResponse authorizationCodeRequest(HttpServletRequest request, IdentityManager identityManager) { AuthorizationCodeGrant grant = new AuthorizationCodeGrant(); try { // Let us parse the authorization request AuthorizationRequest authorizationRequest = parseAuthorizationRequest(request); String responseType = authorizationRequest.getResponseType(); if (responseType.equals(OAuthConstants.CODE) == false) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("response_type should be :code").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } grant.setAuthorizationRequest(authorizationRequest); String passedClientID = authorizationRequest.getClientId(); if (passedClientID == null) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id is null").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } IdentityQuery<Agent> agentQuery = identityManager.createIdentityQuery(Agent.class); agentQuery.setParameter(AttributedType.QUERY_ATTRIBUTE.byName("clientID"), passedClientID); List<Agent> agents = agentQuery.getResultList(); if (agents.size() == 0) { log.error(passedClientID + " not found"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id not found").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } if (agents.size() > 1) { log.error(passedClientID + " multiple found"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("Multiple client_id found").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } Agent clientApp = agents.get(0); // User clientApp = users.get(0); Attribute<String> clientIDAttr = clientApp.getAttribute("clientID"); String clientID = clientIDAttr.getValue(); // check if clientid is valid if (!clientID.equals(passedClientID)) { log.error(passedClientID + " not found"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id not found").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } OAuthResponse oauthResponse = null; String authorizationCode = grant.getValueGenerator().value(); grant.setAuthorizationCode(authorizationCode); clientApp.setAttribute(new Attribute<String>("authorizationCode", authorizationCode)); identityManager.update(clientApp); oauthResponse = grant.authorizationResponse(); oauthResponse.setStatusCode(HttpServletResponse.SC_FOUND); String redirectURI = authorizationRequest.getRedirectUri(); oauthResponse.setLocation(redirectURI + "?" + oauthResponse.asQueryParams()); return oauthResponse; } catch (Exception e) { log.error("Exception:", e); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id not found").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } } /** * Handle Token Request * * @param request * @param identityManager * @return */ public static OAuthResponse tokenRequest(HttpServletRequest request, IdentityManager identityManager) { String grantType = request.getParameter(OAuthConstants.GRANT_TYPE); // Authorization Code Grant if (grantType.equals(AuthorizationCodeGrant.GRANT_TYPE)) { return authorizationCodeGrantTypeTokenRequest(request, identityManager); } if (grantType.equals(OAuthConstants.PASSWORD)) { return passwordGrantTypeTokenRequest(request, identityManager); } if (grantType.equals(OAuthConstants.REFRESH_TOKEN)) { return refreshTokenRequest(request); } return null; } /** * Validate the access token * * @param passedAccessToken * @param identityManager * @return */ public static boolean validateAccessToken(String passedAccessToken, IdentityManager identityManager) { IdentityQuery<Agent> agentQuery = identityManager.createIdentityQuery(Agent.class); agentQuery.setParameter(AttributedType.QUERY_ATTRIBUTE.byName("accessToken"), passedAccessToken); List<Agent> agents = agentQuery.getResultList(); int size = agents.size(); if (size == 0 || size != 1) { return false; } return true; } /** * Parse a {@link ResourceAccessRequest} with application/x-www-form-urlencoded * * @param request * @return */ public static ResourceAccessRequest parseResourceRequest(HttpServletRequest request) { ResourceAccessRequest resourceAccessRequest = new ResourceAccessRequest(); resourceAccessRequest.setAccessToken(request.getParameter(OAuthConstants.ACCESS_TOKEN)); return resourceAccessRequest; } /** * Parse a {@link RegistrationRequest} coming as application/x-www-form-urlencoded * * @param request * @return */ public static RegistrationRequest parseRegistrationRequestWithFORM(HttpServletRequest request) { RegistrationRequest registrationRequest = new RegistrationRequest(); registrationRequest.setClientName(request.getParameter(OAuthConstants.CLIENT_NAME)); registrationRequest.setClientDescription(request.getParameter(OAuthConstants.CLIENT_DESCRIPTION)); registrationRequest.setClient_Icon(request.getParameter(OAuthConstants.CLIENT_ICON)); registrationRequest.setClientUrl(request.getParameter(OAuthConstants.CLIENT_URL)); registrationRequest.setClientRedirecturl(request.getParameter(OAuthConstants.CLIENT_REDIRECT_URL)); return registrationRequest; } /** * Parse a {@link RegistrationRequest} coming as application/json * * @param request * @return */ public static RegistrationRequest parseRegistrationRequestWithJSON(HttpServletRequest request) { // Read JSON from request ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); try { return mapper.readValue(request.getInputStream(), RegistrationRequest.class); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } // Private Methods /** * Refresh Token Request * * @param request * @param identityManager * @return */ private static OAuthResponse refreshTokenRequest(HttpServletRequest request) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("refresh_token not supported").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } /** * Handle Password Grant Type token request * * @param request * @param identityManager * @return */ private static OAuthResponse passwordGrantTypeTokenRequest(HttpServletRequest request, IdentityManager identityManager) { OAuthResponse oauthResponse = null; ResourceOwnerPasswordCredentialsGrant grant = new ResourceOwnerPasswordCredentialsGrant(); PasswordAccessTokenRequest accessTokenRequest = parsePasswordAccessTokenRequest(request); grant.setAccessTokenRequest(accessTokenRequest); String passedClientID = accessTokenRequest.getClientId(); if (passedClientID == null) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id is null").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } String username = accessTokenRequest.getUsername(); String password = accessTokenRequest.getPassword(); UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(); usernamePasswordCredentials.setUsername(username); usernamePasswordCredentials.setPassword(new Password(password.toCharArray())); try { identityManager.validateCredentials(usernamePasswordCredentials); } catch (Exception e) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("invalid username or password").setError(ErrorResponseCode.invalid_grant) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } return oauthResponse; } /** * Handle Token Request * * @param request * @param identityManager * @return */ private static OAuthResponse authorizationCodeGrantTypeTokenRequest(HttpServletRequest request, IdentityManager identityManager) { OAuthResponse oauthResponse = null; AuthorizationCodeGrant grant = new AuthorizationCodeGrant(); AccessTokenRequest accessTokenRequest = parseAccessTokenRequest(request); grant.setAccessTokenRequest(accessTokenRequest); String passedClientID = accessTokenRequest.getClientId(); if (passedClientID == null) { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id is null").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } IdentityQuery<Agent> agentQuery = identityManager.createIdentityQuery(Agent.class); agentQuery.setParameter(AttributedType.QUERY_ATTRIBUTE.byName("clientID"), passedClientID); List<Agent> agents = agentQuery.getResultList(); if (agents.size() == 0) { log.error(passedClientID + " not found"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("passed client_id not found").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } if (agents.size() > 1) { log.error(passedClientID + " multiple found"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("passed client_id multiple found").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } Agent clientApp = agents.get(0); // Get the values from DB Attribute<String> clientIDAttr = clientApp.getAttribute("clientID"); String clientID = clientIDAttr.getValue(); Attribute<String> authorizationCodeAttr = clientApp.getAttribute("authorizationCode"); if (authorizationCodeAttr == null) { log.error("authorization code is null"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("authorization code null").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } String authorizationCode = authorizationCodeAttr.getValue(); // check if clientid is valid if (!clientID.equals(passedClientID)) { log.error("client_id does not match"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("client_id does not match").setError(ErrorResponseCode.invalid_client) .setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } if (accessTokenRequest.getGrantType().equals(AuthorizationCodeGrant.GRANT_TYPE)) { if (!authorizationCode.equals(accessTokenRequest.getCode())) { log.error("authorization_code does not match"); ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorDescription("authorization_code does not match") .setError(ErrorResponseCode.invalid_grant).setStatusCode(HttpServletResponse.SC_BAD_REQUEST); return errorResponse; } } String accessToken = grant.getValueGenerator().value(); clientApp.setAttribute(new Attribute<String>("accessToken", accessToken)); identityManager.update(clientApp); grant.setAccessToken(accessToken); oauthResponse = grant.accessTokenResponse(); oauthResponse.setStatusCode(HttpServletResponse.SC_FOUND); return oauthResponse; } private static AuthorizationRequest parseAuthorizationRequest(HttpServletRequest request) { AuthorizationRequest authorizationRequest = new AuthorizationRequest(); authorizationRequest.setClientId(request.getParameter(OAuthConstants.CLIENT_ID)) .setRedirectUri(request.getParameter(OAuthConstants.REDIRECT_URI)) .setResponseType(request.getParameter(OAuthConstants.RESPONSE_TYPE)); return authorizationRequest; } private static AccessTokenRequest parseAccessTokenRequest(HttpServletRequest request) { AccessTokenRequest accessTokenRequest = new AccessTokenRequest(); accessTokenRequest.setCode(request.getParameter(OAuthConstants.CODE)) .setRedirectUri(request.getParameter(OAuthConstants.REDIRECT_URI)) .setGrantType(request.getParameter(OAuthConstants.GRANT_TYPE)) .setClientId(request.getParameter(OAuthConstants.CLIENT_ID)); return accessTokenRequest; } private static PasswordAccessTokenRequest parsePasswordAccessTokenRequest(HttpServletRequest request) { ResourceOwnerPasswordCredentialsGrant grant = new ResourceOwnerPasswordCredentialsGrant(); PasswordAccessTokenRequest accessTokenRequest = grant.new PasswordAccessTokenRequest(); accessTokenRequest.setPassword(request.getParameter(OAuthConstants.PASSWORD)); accessTokenRequest.setUsername(request.getParameter(OAuthConstants.USERNAME)); accessTokenRequest.setCode(request.getParameter(OAuthConstants.CODE)) .setRedirectUri(request.getParameter(OAuthConstants.REDIRECT_URI)) .setGrantType(request.getParameter(OAuthConstants.GRANT_TYPE)) .setClientId(request.getParameter(OAuthConstants.CLIENT_ID)); return accessTokenRequest; } }