org.openmhealth.shim.OAuth2ShimBase.java Source code

Java tutorial

Introduction

Here is the source code for org.openmhealth.shim.OAuth2ShimBase.java

Source

/*
 * Copyright 2015 Open mHealth
 *
 * 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.openmhealth.shim;

import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.common.util.SerializationUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * Common code for all OAuth2.0 based shims.
 *
 * @author Danilo Bonilla
 */
public abstract class OAuth2ShimBase extends ShimBase implements OAuth2Shim {

    private AuthorizationRequestParametersRepo authorizationRequestParametersRepo;

    private AccessParametersRepo accessParametersRepo;

    protected ShimServerConfig shimServerConfig;

    protected OAuth2ShimBase(ApplicationAccessParametersRepo applicationParametersRepo,
            AuthorizationRequestParametersRepo authorizationRequestParametersRepo,
            AccessParametersRepo accessParametersRepo, ShimServerConfig shimServerConfig) {
        super(applicationParametersRepo);
        this.authorizationRequestParametersRepo = authorizationRequestParametersRepo;
        this.accessParametersRepo = accessParametersRepo;
        this.shimServerConfig = shimServerConfig;
    }

    protected abstract ResponseEntity<ShimDataResponse> getData(OAuth2RestOperations restTemplate,
            ShimDataRequest shimDataRequest) throws ShimException;

    protected String getCallbackUrl() {
        return shimServerConfig.getCallbackUrl(getShimKey());
    }

    @Override
    public AuthorizationRequestParameters getAuthorizationRequestParameters(String username,
            Map<String, String> addlParameters) throws ShimException {
        OAuth2RestOperations restTemplate = restTemplate();
        try {
            trigger(restTemplate, getTriggerDataRequest());
            return AuthorizationRequestParameters.authorized();
        } catch (UserRedirectRequiredException e) {
            /**
             * If an exception was thrown it means a redirect is required
             * for user's external authorization with toolmaker.
             */
            AccessTokenRequest accessTokenRequest = restTemplate.getOAuth2ClientContext().getAccessTokenRequest();
            String stateKey = accessTokenRequest.getStateKey();

            /**
             * Build an authorization request from the exception
             * parameters. We also serialize spring's accessTokenRequest.
             */
            AuthorizationRequestParameters authRequestParams = new AuthorizationRequestParameters();
            authRequestParams.setRedirectUri(e.getRedirectUri());
            authRequestParams.setStateKey(e.getStateKey());
            authRequestParams.setAuthorizationUrl(getAuthorizationUrl(e));
            authRequestParams.setSerializedRequest(SerializationUtils.serialize(accessTokenRequest));
            authRequestParams.setStateKey(stateKey);

            authorizationRequestParametersRepo.save(authRequestParams);
            return authRequestParams;
        }
    }

    protected abstract String getAuthorizationUrl(UserRedirectRequiredException exception);

    public OAuth2ProtectedResourceDetails getResource() {
        ApplicationAccessParameters parameters = findApplicationAccessParameters();
        AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
        resource.setAccessTokenUri(getBaseTokenUrl());
        resource.setUserAuthorizationUri(getBaseAuthorizeUrl());
        resource.setClientId(parameters.getClientId());
        resource.setScope(getScopes());
        resource.setClientSecret(parameters.getClientSecret());
        resource.setTokenName("access_token");
        resource.setGrantType("authorization_code");
        resource.setUseCurrentUri(true);
        return resource;
    }

    /**
     * Request parameters to be used when 'triggering'
     * spring oauth2. This should be the equivalent
     * of a ping to the external data provider.
     *
     * @return - The Shim data request to use for trigger.
     */
    protected ShimDataRequest getTriggerDataRequest() {
        ShimDataRequest shimDataRequest = new ShimDataRequest();
        shimDataRequest.setDataTypeKey(getShimDataTypes()[0].toString());
        shimDataRequest.setNumToReturn(1l);
        return shimDataRequest;
    }

    @Override
    public AuthorizationResponse handleAuthorizationResponse(HttpServletRequest servletRequest)
            throws ShimException {

        String state = servletRequest.getParameter("state");
        String code = servletRequest.getParameter("code");

        AuthorizationRequestParameters authorizationRequestParameters = authorizationRequestParametersRepo
                .findByStateKey(state);

        if (authorizationRequestParameters == null) {
            throw new IllegalStateException(
                    "Could not find corresponding authorization " + "request parameters, cannot continue.");
        }

        OAuth2RestOperations restTemplate = restTemplate(state, code);
        try {
            /**
             * Create a persistable access parameters entity so that
             * spring oauth2's client token services can relate
             * the serialized OAuth2AccessToken to it.
             */
            AccessParameters accessParameters = new AccessParameters();
            accessParameters.setUsername(authorizationRequestParameters.getUsername());
            accessParameters.setShimKey(getShimKey());
            accessParameters.setStateKey(state);
            accessParametersRepo.save(accessParameters);

            trigger(restTemplate, getTriggerDataRequest());

            /**
             * By this line we will have an approved access token or
             * not, if we do not then we delete the access parameters entity.
             */
            if (restTemplate.getAccessToken() == null) {
                accessParametersRepo.delete(accessParameters);
                return AuthorizationResponse.error("Did not receive approval");
            } else {
                accessParameters = accessParametersRepo.findByUsernameAndShimKey(
                        authorizationRequestParameters.getUsername(), getShimKey(),
                        new Sort(Sort.Direction.DESC, "dateCreated"));
            }
            return AuthorizationResponse.authorized(accessParameters);
        } catch (OAuth2Exception e) {
            //TODO: OAuth2Exception may include other stuff
            System.out.println("Problem trying out the token!");
            e.printStackTrace();
            return AuthorizationResponse.error(e.getMessage());
        }
    }

    @Override
    public ShimDataResponse getData(ShimDataRequest shimDataRequest) throws ShimException {
        return getData(restTemplate(), shimDataRequest).getBody();
    }

    public void trigger(OAuth2RestOperations restTemplate, ShimDataRequest shimDataRequest) throws ShimException {
        getData(restTemplate, shimDataRequest);
    }

    protected OAuth2RestOperations restTemplate(String stateKey, String code) {

        DefaultAccessTokenRequest existingRequest =

                stateKey != null && authorizationRequestParametersRepo.findByStateKey(stateKey) != null ?

                        (DefaultAccessTokenRequest) SerializationUtils.deserialize(
                                authorizationRequestParametersRepo.findByStateKey(stateKey).getSerializedRequest())
                        : null;

        if (existingRequest != null && code != null) {
            existingRequest.set("code", code);
        }

        DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext(
                existingRequest != null ? existingRequest : new DefaultAccessTokenRequest());

        if (existingRequest != null) {
            context.setPreservedState(stateKey, "NONE");
        }

        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(getResource(), context);
        AccessTokenProviderChain tokenProviderChain = new AccessTokenProviderChain(
                new ArrayList<>(Arrays.asList(getAuthorizationCodeAccessTokenProvider())));
        tokenProviderChain.setClientTokenServices(new AccessParameterClientTokenServices(accessParametersRepo));
        restTemplate.setAccessTokenProvider(tokenProviderChain);
        return restTemplate;
    }

    protected OAuth2RestOperations restTemplate() {
        return restTemplate(null, null);
    }
}