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

com.sap.cloud.sdk.cloudplatform.connectivity.XsuaaService Maven / Gradle / Ivy

/*
 * Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved.
 */

package com.sap.cloud.sdk.cloudplatform.connectivity;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import org.slf4j.Logger;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.sap.cloud.sdk.cloudplatform.CloudPlatform;
import com.sap.cloud.sdk.cloudplatform.CloudPlatformAccessor;
import com.sap.cloud.sdk.cloudplatform.ScpCfCloudPlatform;
import com.sap.cloud.sdk.cloudplatform.cache.CacheKey;
import com.sap.cloud.sdk.cloudplatform.cache.CacheManager;
import com.sap.cloud.sdk.cloudplatform.exception.CloudPlatformException;
import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException;
import com.sap.cloud.sdk.cloudplatform.logging.CloudLoggerFactory;
import com.sap.cloud.sdk.cloudplatform.security.BasicCredentials;
import com.sap.cloud.sdk.cloudplatform.security.user.exception.UserAccessException;
import com.sap.cloud.sdk.cloudplatform.security.user.exception.UserNotAuthenticatedException;
import com.sap.cloud.sdk.cloudplatform.tenant.exception.TenantAccessException;
import com.sap.cloud.sdk.cloudplatform.tenant.exception.TenantNotAvailableException;

class XsuaaService
{
    private static final Logger logger = CloudLoggerFactory.getSanitizedLogger(XsuaaService.class);

    static final String ERROR_BIND_XSUAA_SERVICE =
        "Please make sure to correctly bind your application to a XSUAA service instance.";

    private static final String SERVICE_CLIENT_ID = "clientid";
    private static final String SERVICE_CLIENT_SECRET = "clientsecret";

    static CacheBuilder tokenCacheBuilder =
        CacheBuilder.newBuilder().concurrencyLevel(10).maximumSize(100000).expireAfterAccess(60, TimeUnit.MINUTES);

    static Cache tokenCache = CacheManager.register(tokenCacheBuilder.build());

    private ScpCfCloudPlatform getCloudPlatform()
    {
        final CloudPlatform cloudPlatform = CloudPlatformAccessor.getCloudPlatform();

        if( !(cloudPlatform instanceof ScpCfCloudPlatform) ) {
            throw new ShouldNotHappenException(
                "The current Cloud platform is not an instance of "
                    + ScpCfCloudPlatform.class.getSimpleName()
                    + ". Please make sure to specify a dependency to com.sap.cloud.s4hana.cloudplatform:core-scp-cf.");
        }

        return (ScpCfCloudPlatform) cloudPlatform;
    }

    private URI getXsuaaUri()
        throws TokenRequestFailedException
    {
        final JsonObject xsuaaCredentials;
        try {
            xsuaaCredentials = getCloudPlatform().getXsuaaServiceCredentials();
        }
        catch( final CloudPlatformException e ) {
            throw new TokenRequestFailedException(e);
        }

        @Nullable
        final JsonElement xsuaaUri = xsuaaCredentials.get("url");

        if( xsuaaUri == null || !xsuaaUri.isJsonPrimitive() ) {
            throw new TokenRequestFailedException("Failed to get XSUAA service URI. " + ERROR_BIND_XSUAA_SERVICE);
        }

        try {
            return new URI(xsuaaUri.getAsString());
        }
        catch( final URISyntaxException e ) {
            throw new TokenRequestFailedException(e);
        }
    }

    private BasicCredentials getClientCredentials( final String serviceName )
        throws TokenRequestFailedException
    {
        final JsonObject serviceCredentials;
        try {
            serviceCredentials = getCloudPlatform().getServiceCredentials(serviceName);
        }
        catch( final CloudPlatformException e ) {
            throw new TokenRequestFailedException(e);
        }

        @Nullable
        final JsonElement clientIdElement = serviceCredentials.get(SERVICE_CLIENT_ID);

        @Nullable
        final JsonElement clientSecretElement = serviceCredentials.get(SERVICE_CLIENT_SECRET);

        if( clientIdElement == null
            || !clientIdElement.isJsonPrimitive()
            || clientSecretElement == null
            || !clientSecretElement.isJsonPrimitive() ) {
            throw new TokenRequestFailedException(
                "Failed to get "
                    + serviceName
                    + " service client identifier and secret. "
                    + "Please make sure to correctly bind your application to a "
                    + serviceName
                    + " service instance.");
        }

        final String clientId = clientIdElement.getAsString();
        final String clientSecret = clientSecretElement.getAsString();

        return new BasicCredentials(clientId, clientSecret);
    }

    private AccessToken requestAccessToken(
        final URI xsuaaUri,
        final BasicCredentials clientCredentials,
        final boolean propagateUser,
        final boolean useProviderTenant )
        throws TokenRequestFailedException,
            TokenRequestDeniedException
    {
        final TokenRequest tokenRequest = new TokenRequest();

        if( propagateUser ) {
            return tokenRequest.requestTokenWithUserTokenGrant(xsuaaUri, clientCredentials, useProviderTenant);
        } else {
            return tokenRequest.requestTokenWithClientCredentialsGrant(xsuaaUri, clientCredentials, useProviderTenant);
        }
    }

    CacheKey getCacheKey( final String serviceName, final boolean propagateUser, final boolean useProviderTenant )
        throws TokenRequestFailedException
    {
        final CacheKey cacheKey;

        try {
            if( propagateUser ) {
                cacheKey = CacheKey.ofTenantAndUserIsolation();
            } else {
                cacheKey = CacheKey.ofTenantIsolation();
            }
        }
        catch( final
            TenantNotAvailableException
                | TenantAccessException
                | UserNotAuthenticatedException
                | UserAccessException e ) {
            throw new TokenRequestFailedException("Failed to determine cache key.", e);
        }

        return cacheKey.append(serviceName, useProviderTenant);
    }

    AccessToken
        getServiceToken( final String serviceName, final boolean propagateUser, final boolean useProviderTenant )
            throws TokenRequestFailedException,
                TokenRequestDeniedException
    {
        final CacheKey cacheKey = getCacheKey(serviceName, propagateUser, useProviderTenant);

        // Note: strict cache synchronization is not required here.
        // In the worst case, the token is fetched multiple times and the freshest one is cached.

        @Nullable
        final AccessToken accessToken = tokenCache.getIfPresent(cacheKey);

        if( accessToken != null && accessToken.isValid() ) {
            if( logger.isDebugEnabled() ) {
                logger.debug(
                    "Returning access token from cache (cache key: "
                        + cacheKey
                        + ", token expiry: "
                        + accessToken.getExpiry()
                        + ").");
            }
            return accessToken;
        }

        if( logger.isDebugEnabled() ) {
            logger.debug("Requesting fresh access token for cache key " + cacheKey + ".");
        }

        final URI xsuaaUri = getXsuaaUri();
        final BasicCredentials clientCredentials = getClientCredentials(serviceName);

        final AccessToken freshToken =
            requestAccessToken(xsuaaUri, clientCredentials, propagateUser, useProviderTenant);

        tokenCache.put(cacheKey, freshToken);

        return freshToken;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy