
com.sap.cloud.security.adapter.spring.SAPOfflineTokenServicesCloud Maven / Gradle / Ivy
package com.sap.cloud.security.adapter.spring;
import com.sap.cloud.security.config.Environments;
import com.sap.cloud.security.config.OAuth2ServiceConfiguration;
import com.sap.cloud.security.config.cf.CFConstants;
import com.sap.cloud.security.token.*;
import com.sap.cloud.security.token.validation.ValidationResult;
import com.sap.cloud.security.token.validation.Validator;
import com.sap.cloud.security.token.validation.validators.JwtValidatorBuilder;
import com.sap.cloud.security.xsuaa.Assertions;
import com.sap.cloud.security.xsuaa.client.SpringOAuth2TokenKeyService;
import com.sap.cloud.security.xsuaa.client.SpringOidcConfigurationService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.stream.Collectors;
/**
* This constructor requires a dependency to Spring-security oauth, which will
* be deprecated soon.
*
*
* {@code
*
* org.springframework.security.oauth
* spring-security-oauth2
* provided
*
*
* org.springframework
* spring-beans
* provided
*
* }
*
*
* By default it used Apache Rest Client for communicating with the OAuth2
* Server.
*
* Spring Security framework initializes the
* {@link org.springframework.security.core.context.SecurityContext} with the
* {@code OAuth2Authentication} which is provided as part of
* {@link #loadAuthentication} method.
* This gives you the following options:
*
* - All Spring security features are supported that uses
* {@link org.springframework.security.core.context.SecurityContext#getAuthentication()}
* - You can access the {@code Authentication} via
* {@link SecurityContextHolder#getContext()} also within asynchronous
* threads.
* - You can access the {@code Token} via
* {@link SpringSecurityContext#getToken()} also within asynchronous
* threads.
*
*
*/
public class SAPOfflineTokenServicesCloud implements ResourceServerTokenServices, InitializingBean {
private final OAuth2ServiceConfiguration serviceConfiguration;
private Validator tokenValidator;
private JwtValidatorBuilder jwtValidatorBuilder;
private boolean useLocalScopeAsAuthorities;
private ScopeConverter xsuaaScopeConverter;
/**
* Constructs an instance which is preconfigured for XSUAA service configuration
* from SAP CP Environment.
*/
public SAPOfflineTokenServicesCloud() {
this(Environments.getCurrent().getXsuaaConfiguration());
}
/**
* Constructs an instance with custom configuration.
*
* @param serviceConfiguration
* the service configuration. You can use
* {@link com.sap.cloud.security.config.Environments} in order to
* load service configuration from the binding information in your
* environment.
*/
public SAPOfflineTokenServicesCloud(OAuth2ServiceConfiguration serviceConfiguration) {
this(serviceConfiguration, new RestTemplate());
}
/**
* Constructs an instance with custom configuration and rest template.
*
* @param serviceConfiguration
* the service configuration. You can use
* {@link com.sap.cloud.security.config.Environments} in order to
* load service configuration from the binding information in your
* environment.
* @param restOperations
* the spring rest template
*/
public SAPOfflineTokenServicesCloud(OAuth2ServiceConfiguration serviceConfiguration,
RestOperations restOperations) {
this(serviceConfiguration, JwtValidatorBuilder.getInstance(serviceConfiguration)
.withOAuth2TokenKeyService(new SpringOAuth2TokenKeyService(restOperations))
.withOidcConfigurationService(new SpringOidcConfigurationService(restOperations)));
}
SAPOfflineTokenServicesCloud(OAuth2ServiceConfiguration serviceConfiguration,
JwtValidatorBuilder jwtValidatorBuilder) {
Assertions.assertNotNull(serviceConfiguration, "serviceConfiguration is required.");
Assertions.assertNotNull(jwtValidatorBuilder, "jwtValidatorBuilder is required.");
this.serviceConfiguration = serviceConfiguration;
this.jwtValidatorBuilder = jwtValidatorBuilder;
if (serviceConfiguration.hasProperty(CFConstants.XSUAA.APP_ID)) {
this.xsuaaScopeConverter = new XsuaaScopeConverter(
serviceConfiguration.getProperty(CFConstants.XSUAA.APP_ID));
}
}
/**
* Configure another XSUAA instance, e.g. of plan broker.
*
* @param otherServiceConfiguration
* another service configuration. You can use
* {@link com.sap.cloud.security.config.cf.CFEnvironment#getXsuaaConfigurationForTokenExchange()}
* in order to load additional broker service configuration from the
* binding information in your environment.
* @return the instance itself
*/
public SAPOfflineTokenServicesCloud withAnotherServiceConfiguration(
OAuth2ServiceConfiguration otherServiceConfiguration) {
jwtValidatorBuilder.configureAnotherServiceInstance(otherServiceConfiguration);
return this;
}
@Override
public OAuth2Authentication loadAuthentication(@Nonnull String accessToken)
throws AuthenticationException, InvalidTokenException {
Token token = checkAndCreateToken(accessToken);
ValidationResult validationResult = tokenValidator.validate(token);
if (validationResult.isErroneous()) {
throw new InvalidTokenException(validationResult.getErrorDescription());
}
SecurityContext.setToken(token);
return getOAuth2Authentication(serviceConfiguration.getClientId(), getScopes(token));
}
static OAuth2Authentication getOAuth2Authentication(String clientId, Set scopes) {
Authentication userAuthentication = null; // TODO no SAPUserDetails support. Using spring alternative?
final AuthorizationRequest authorizationRequest = new AuthorizationRequest(clientId, scopes);
authorizationRequest.setAuthorities(getAuthorities(scopes));
authorizationRequest.setApproved(true);
return new OAuth2Authentication(authorizationRequest.createOAuth2Request(), userAuthentication);
}
private Set getScopes(Token token) {
Set scopes = token instanceof AccessToken
? ((AccessToken) token).getScopes()
: Collections.emptySet();
if (useLocalScopeAsAuthorities) {
scopes = xsuaaScopeConverter.convert(scopes);
}
return scopes;
}
@Override
public void afterPropertiesSet() {
tokenValidator = jwtValidatorBuilder.build();
}
@Override
public OAuth2AccessToken readAccessToken(String accessToken) {
throw new UnsupportedOperationException("Not supported: readAccessToken()");
}
/**
* This method allows to overwrite the default behavior of the authorities
* converter implementation.
*
* @param extractLocalScopesOnly
* true when only local scopes are extracted. Local scopes means that
* non-application specific scopes are filtered out and scopes are
* returned without appId prefix, e.g. "Display".
* @return the token authenticator itself
*/
public SAPOfflineTokenServicesCloud setLocalScopeAsAuthorities(boolean extractLocalScopesOnly) {
this.useLocalScopeAsAuthorities = extractLocalScopesOnly;
return this;
}
private static Set getAuthorities(Collection scopes) {
return scopes.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet());
}
private Token checkAndCreateToken(@Nonnull String accessToken) {
try {
switch (serviceConfiguration.getService()) {
case XSUAA:
return new XsuaaToken(accessToken).withScopeConverter(xsuaaScopeConverter);
case IAS:
return new SapIdToken(accessToken);
default:
// TODO support IAS
throw new InvalidTokenException(
"AccessToken of service " + serviceConfiguration.getService() + " is not supported.");
}
} catch (Exception e) {
throw new InvalidTokenException(e.getMessage());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy