All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.covisint.platform.oauth.client.token.sdk.TokenSupplier Maven / Gradle / Ivy

/* 
 * Copyright 2015 Covisint
 * 
 * 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 com.covisint.platform.oauth.client.token.sdk;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.covisint.core.http.service.client.BaseResourceClientFactory;
import com.covisint.core.http.service.client.ClientInitializationContext;
import com.covisint.core.support.constraint.Nonnull;
import com.covisint.core.support.constraint.NotEmpty;
import com.covisint.platform.oauth.client.token.sdk.TokenSDK.TokenClient;
import com.covisint.platform.oauth.core.token.Token;
import com.google.common.base.Supplier;

/**
 * Supplier of access tokens retrieved from the {@link TokenClient}. This supplier is configured to use an
 * interchangeable {@link AuthConfigurationProvider} through the use of
 * {@link SwappableCredentialSupport#swapCredentials(String, String, String)}. This adds support for pluggable
 * credentials during runtime, which may be useful during cases where app-level access tokens need to be replaced with
 * user-level access tokens.
 * 
 * @see SwappableCredentialSupport#swapCredentials(String, String, String)
 */
public class TokenSupplier implements SwappableCredentialSupport, Supplier {

    /**
     * The number of milliseconds to execute a token refresh in advance of the existing token's expiration instant. For
     * example, if the expiration instant is x and the advance refresh window is y, the refresh instant will be (x - y).
     */
    private static final long EXPIRATION_ADVANCE_REFRESH_MILLIS = 60000;

    /** Default refresh window of 15 min. */
    private static final long DEFAULT_REFRESH_DELAY = 60000 * 15;

    /** The name to use for the timer thread. */
    private static final String TIMER_NAME = "Access Token Refresher";

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(TokenSupplier.class);

    /** The token client used to fetch new access tokens. */
    private final TokenClient tokenClient;

    /** Timer used to schedule token refreshes. */
    private final Timer timer;

    /** The auth service base URL. */
    private final URL authServiceBaseUrl;

    /** The auth config provider, needed when using the token client to retrieve tokens. */
    private AuthConfigurationProvider authConfigurationProvider;

    /** The currently active token. */
    private Token token;

    /**
     * Initializing constructor.
     *
     * @param provider The auth config provider to set.
     */
    public TokenSupplier(@Nonnull AuthConfigurationProvider provider) {

        authConfigurationProvider = provider;

        timer = new Timer(TIMER_NAME, true);

        final String authUrl = provider.getAuthServiceBaseUrl().toString();
        final BaseResourceClientFactory clientFactory = ClientInitializationContext.getClientFactory();

        final TokenSDK tokenSdk = new TokenSDK(authUrl);
        tokenSdk.setAuthConfigProvider(provider);

        if (clientFactory != null) {
            copyConfig(tokenSdk, clientFactory);
        }

        tokenClient = tokenSdk.newClient();

        try {
            authServiceBaseUrl = new URL(authUrl);
        } catch (MalformedURLException e) {
            throw new IllegalStateException("Bad auth service URL", e);
        }

        refreshToken();
    }

    /**
     * Copies the client configuration settings from source to target.
     * 
     * @param target The target.
     * @param source The source.
     */
    private void copyConfig(@Nonnull BaseResourceClientFactory target, @Nonnull BaseResourceClientFactory source) {

        if (source.getHttpProxySpec() != null) {
            target.setHttpProxySpec(source.getHttpProxySpec());
        }

        if (source.getSourceAddress() != null) {
            target.setSourceAddress(source.getSourceAddress());
        }

        if (source.getContentCompressionEnabled() != null) {
            target.setContentCompressionEnabled(source.getContentCompressionEnabled());
        }

        if (source.getConnectionTimeout() != null) {
            target.setConnectionTimeout(source.getConnectionTimeout());
        }

        if (source.getSocketTimeout() != null) {
            target.setSocketTimeout(source.getSocketTimeout());
        }

        if (source.getContentCharSet() != null) {
            target.setContentCharSet(source.getContentCharSet());
        }

    }

    /**
     * Refreshes the current access token (which may be null) and schedules the next refresh based on calculated
     * expiration time.
     */
    private void refreshToken() {

        final String clientId = authConfigurationProvider.getClientId();
        final String clientSecret = authConfigurationProvider.getClientSecret();

        log.debug("Fetching new token for client id {}", clientId);

        token = tokenClient.getToken(clientId, clientSecret).checkedGet();

        scheduleNextRefresh();
    }

    /** Schedules the next refresh in advance of the current token's expiration instant. */
    private void scheduleNextRefresh() {
        long refreshDelay = token.getExpirationTime() - System.currentTimeMillis() - EXPIRATION_ADVANCE_REFRESH_MILLIS;

        if (refreshDelay < EXPIRATION_ADVANCE_REFRESH_MILLIS) {
            log.warn("Invalid refresh delay: {}.  Using default: {}", refreshDelay, DEFAULT_REFRESH_DELAY);
            refreshDelay = DEFAULT_REFRESH_DELAY;
        }

        timer.schedule(new TokenRefresher(), refreshDelay);
    }

    /** {@inheritDoc} */
    public String get() {
        return token.getEncodedToken();
    }

    /** {@inheritDoc} */
    public void swapCredentials(@Nonnull @NotEmpty String clientId, @Nonnull @NotEmpty String secret, String appId) {
        synchronized (authConfigurationProvider) {
            authConfigurationProvider = new BasicAuthConfigurationProvider(clientId, secret, appId, authServiceBaseUrl);
        }
    }

    /** Timer task responsible for refreshing the access token. */
    private final class TokenRefresher extends TimerTask {

        /** {@inheritDoc} */
        public void run() {
            refreshToken();
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy