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

io.pivotal.spring.cloud.config.client.OAuth2ConfigDataLocationResolver Maven / Gradle / Ivy

There is a newer version: 4.3.0
Show newest version
/*
 * Copyright 2023-2024 the original author or authors.
 *
 * 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
 *
 *      https://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 io.pivotal.spring.cloud.config.client;

import org.apache.commons.logging.Log;
import org.springframework.boot.context.config.ConfigDataLocation;
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
import org.springframework.boot.context.config.ConfigDataLocationResolver;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.ConfigClientRequestTemplateFactory;
import org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver;
import org.springframework.cloud.config.client.ConfigServerConfigDataResource;
import org.springframework.core.Ordered;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;

/**
 * Using oauth2 properties to configure an authorization interceptor for the
 * RestTemplate that calls config server.
 * 

* Note: Despite implementing {@link ConfigDataLocationResolver}, this class does not * resolve any location. It only configures and registers the * {@link ConfigClientRequestTemplateFactory} and RestTemplate which later * will be used by {@link ConfigServerConfigDataLocationResolver} for calling config * server. *

* Finally, it registers the RestTemplate bean to be consumed by * {@link ConfigResourceClientAutoConfiguration} and * {@link VaultTokenRenewalAutoConfiguration} after application startup. */ public class OAuth2ConfigDataLocationResolver implements ConfigDataLocationResolver, Ordered { private final Log log; public OAuth2ConfigDataLocationResolver(DeferredLogFactory factory) { this.log = factory.getLog(OAuth2ConfigDataLocationResolver.class); } @Override public boolean isResolvable(ConfigDataLocationResolverContext resolverContext, ConfigDataLocation location) { if (!location.hasPrefix(ConfigServerConfigDataLocationResolver.PREFIX)) { return false; } var binder = resolverContext.getBinder(); var isConfigEnabled = binder.bind(ConfigClientProperties.PREFIX + ".enabled", Boolean.class).orElse(true); if (!isConfigEnabled) { return false; } var bootstrapContext = resolverContext.getBootstrapContext(); var oAuth2Properties = binder.bind(ConfigClientOAuth2Properties.PREFIX, ConfigClientOAuth2Properties.class) .orElse(null); var clientProperties = binder.bind(ConfigClientProperties.PREFIX, ConfigClientProperties.class) .orElse(new ConfigClientProperties(new StandardEnvironment())); // Register the custom factory with oauth2 interceptor. bootstrapContext.registerIfAbsent(ConfigClientRequestTemplateFactory.class, context -> new OAuth2ConfigClientRequestTemplateFactory(this.log, clientProperties, oAuth2Properties)); var factory = (OAuth2ConfigClientRequestTemplateFactory) bootstrapContext .get(ConfigClientRequestTemplateFactory.class); // Update the factory, in case it was registered earlier factory.update(clientProperties, oAuth2Properties); // Register the template with oauth2 interceptor. bootstrapContext.registerIfAbsent(RestTemplate.class, context -> factory.create()); var template = bootstrapContext.get(RestTemplate.class); // Update the template, in case it was registered earlier factory.updateTemplate(template); // Add the RestTemplate as bean, once the startup is finished. bootstrapContext.addCloseListener(event -> event.getApplicationContext() .getBeanFactory() .registerSingleton("configClientRestTemplate", event.getBootstrapContext().get(RestTemplate.class))); return false; } @Override public List resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location) throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException { throw new IllegalStateException("Unexpected call. This resolver should not resolve any location"); } @Override public List resolveProfileSpecific(ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) throws ConfigDataLocationNotFoundException { throw new IllegalStateException("Unexpected call. This resolver should not resolve any location"); } /** * It should be registered before {@link ConfigServerConfigDataLocationResolver}. See * {@link ConfigServerConfigDataLocationResolver#getOrder()} */ @Override public int getOrder() { return -2; } private static class OAuth2ConfigClientRequestTemplateFactory extends ConfigClientRequestTemplateFactory { private ConfigClientProperties properties; private ConfigClientOAuth2Properties oAuth2Properties; public OAuth2ConfigClientRequestTemplateFactory(Log log, ConfigClientProperties clientProperties, ConfigClientOAuth2Properties oAuth2Properties) { super(log, clientProperties); this.properties = clientProperties; this.oAuth2Properties = oAuth2Properties; } public void update(ConfigClientProperties clientProperties, ConfigClientOAuth2Properties oAuth2Properties) { this.properties = clientProperties; this.oAuth2Properties = oAuth2Properties; } @Override public ConfigClientProperties getProperties() { return this.properties; } @Override public RestTemplate create() { if (properties.getRequestReadTimeout() < 0) { throw new IllegalStateException("Invalid Value for Read Timeout set."); } if (properties.getRequestConnectTimeout() < 0) { throw new IllegalStateException("Invalid Value for Connect Timeout set."); } return updateTemplate(new RestTemplate()); } @Override public void addAuthorizationToken(HttpHeaders httpHeaders, String username, String password) { String authorization = properties.getHeaders().get(AUTHORIZATION); if (password != null && authorization != null) { throw new IllegalStateException("You must set either 'password' or 'authorization'"); } if (password != null) { byte[] token = java.util.Base64.getEncoder().encode((username + ":" + password).getBytes()); httpHeaders.add("Authorization", "Basic " + new String(token)); } else if (authorization != null) { httpHeaders.add("Authorization", authorization); } } RestTemplate updateTemplate(RestTemplate template) { template.setRequestFactory(createHttpRequestFactory(properties)); var interceptors = new ArrayList(); var headers = new HashMap<>(properties.getHeaders()); headers.remove(AUTHORIZATION); // To avoid redundant addition of header if (!headers.isEmpty()) { interceptors.add(new GenericRequestHeaderInterceptor(headers)); } if (this.oAuth2Properties != null) { var clientRegistration = ClientRegistration.withRegistrationId("config-client") .clientId(oAuth2Properties.getClientId()) .clientSecret(oAuth2Properties.getClientSecret()) .tokenUri(oAuth2Properties.getAccessTokenUri()) .scope(oAuth2Properties.getScope()) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .build(); interceptors.add(new OAuth2AuthorizedClientHttpRequestInterceptor(clientRegistration)); } template.setInterceptors(interceptors); return template; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy