Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/spring/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Upgrade Spring Boot dependencies version to 2.7.4 and Spring Cloud dependencies

#### Bugs Fixed
- Fix bug: Put a value into Collections.emptyMap(). [#31190](https://github.com/Azure/azure-sdk-for-java/issues/31190).
- Fix bug: RestOperations is not well configured when jwkResolver is null. [#31218](https://github.com/Azure/azure-sdk-for-java/issues/31218).
- Fix bug: Duplicated "scope" parameter. [#31191](https://github.com/Azure/azure-sdk-for-java/issues/31191).

## 4.4.0 (2022-09-26)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ JwtBearerOAuth2AuthorizedClientProvider azureAdJwtBearerProvider(
ObjectProvider<OAuth2ClientAuthenticationJwkResolver> resolvers) {
JwtBearerOAuth2AuthorizedClientProvider provider = new JwtBearerOAuth2AuthorizedClientProvider();
OAuth2ClientAuthenticationJwkResolver resolver = resolvers.getIfUnique();
AadJwtBearerGrantRequestEntityConverter jwtBearerConverter = new AadJwtBearerGrantRequestEntityConverter();
DefaultJwtBearerTokenResponseClient client = new DefaultJwtBearerTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
AadJwtBearerGrantRequestEntityConverter converter = new AadJwtBearerGrantRequestEntityConverter();
if (resolver != null) {
jwtBearerConverter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));
}
DefaultJwtBearerTokenResponseClient responseClient = new DefaultJwtBearerTokenResponseClient();
responseClient.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
responseClient.setRequestEntityConverter(jwtBearerConverter);
provider.setAccessTokenResponseClient(responseClient);
client.setRequestEntityConverter(converter);
provider.setAccessTokenResponseClient(client);
return provider;
}

Expand All @@ -159,47 +159,42 @@ RefreshTokenOAuth2AuthorizedClientProvider azureRefreshTokenProvider(
ObjectProvider<OAuth2ClientAuthenticationJwkResolver> resolvers) {
RefreshTokenOAuth2AuthorizedClientProvider provider = new RefreshTokenOAuth2AuthorizedClientProvider();
OAuth2ClientAuthenticationJwkResolver resolver = resolvers.getIfUnique();
DefaultRefreshTokenTokenResponseClient client = new DefaultRefreshTokenTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
Comment thread
chenrujun marked this conversation as resolved.
if (resolver != null) {
OAuth2RefreshTokenGrantRequestEntityConverter converter = new OAuth2RefreshTokenGrantRequestEntityConverter();
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultRefreshTokenTokenResponseClient responseClient = new DefaultRefreshTokenTokenResponseClient();
responseClient.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
responseClient.setRequestEntityConverter(converter);
provider.setAccessTokenResponseClient(responseClient);
client.setRequestEntityConverter(converter);
}
Comment thread
chenrujun marked this conversation as resolved.
provider.setAccessTokenResponseClient(client);
return provider;
}

private void passwordGrantBuilderAccessTokenResponseClientCustomizer(
OAuth2AuthorizedClientProviderBuilder.PasswordGrantBuilder builder,
OAuth2ClientAuthenticationJwkResolver resolver) {
DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
if (resolver != null) {
OAuth2PasswordGrantRequestEntityConverter converter = new OAuth2PasswordGrantRequestEntityConverter();
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
client.setRequestEntityConverter(converter);

builder.accessTokenResponseClient(client);
}
builder.accessTokenResponseClient(client);
}
Comment thread
chenrujun marked this conversation as resolved.

private void clientCredentialsGrantBuilderAccessTokenResponseClientCustomizer(
OAuth2AuthorizedClientProviderBuilder.ClientCredentialsGrantBuilder builder,
OAuth2ClientAuthenticationJwkResolver resolver) {
DefaultClientCredentialsTokenResponseClient client = new DefaultClientCredentialsTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
if (resolver != null) {
OAuth2ClientCredentialsGrantRequestEntityConverter converter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
converter.addParametersConverter(new AadJwtClientAuthenticationParametersConverter<>(resolver::resolve));

DefaultClientCredentialsTokenResponseClient client = new DefaultClientCredentialsTokenResponseClient();
client.setRestOperations(createOAuth2AccessTokenResponseClientRestTemplate(restTemplateBuilder));
client.setRequestEntityConverter(converter);

builder.accessTokenResponseClient(client);
}
builder.accessTokenResponseClient(client);
}
Comment thread
chenrujun marked this conversation as resolved.

private AadAzureDelegatedOAuth2AuthorizedClientProvider azureDelegatedOAuth2AuthorizedClientProvider(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
import com.azure.spring.cloud.autoconfigure.context.AzureTokenCredentialAutoConfiguration;
import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver;
import com.azure.spring.cloud.core.implementation.factory.AbstractAzureServiceClientBuilderFactory;
import com.azure.spring.cloud.core.implementation.util.ReflectionUtils;
import com.azure.spring.cloud.core.properties.AzureProperties;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

import static com.azure.spring.cloud.core.implementation.util.ReflectionUtils.getField;
import static org.assertj.core.api.Assertions.assertThat;

public abstract class AbstractAzureServiceConfigurationTests<T extends AbstractAzureServiceClientBuilderFactory<?>,
Expand Down Expand Up @@ -105,12 +105,12 @@ private <C> void assertSovereignCloudsSetInCredential(AssertableApplicationConte
}

private AzureTokenCredentialResolver getAzureTokenCredentialResolver(T builderFactory) {
return (AzureTokenCredentialResolver) ReflectionUtils.getField(getBuilderFactoryType(),
return (AzureTokenCredentialResolver) getField(getBuilderFactoryType(),
"tokenCredentialResolver", builderFactory);
}

private IdentityClient getIdentityClient(TokenCredential credential) {
return (IdentityClient) ReflectionUtils.getField(credential.getClass(), "identityClient", credential);
return (IdentityClient) getField(credential.getClass(), "identityClient", credential);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,19 @@
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;

import static com.azure.spring.cloud.autoconfigure.aad.implementation.AadRestTemplateCreator.createOAuth2AccessTokenResponseClientRestTemplate;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.AadRestTemplateCreator.createOAuth2ErrorResponseHandledRestTemplate;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.AadRestTemplateCreator.createRestTemplate;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.AadRestTemplateCreatorTest.RestTemplateProxyCustomizerConfiguration.FACTORY;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.RestTemplateProxyCustomizerConfiguration.FACTORY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
Expand Down Expand Up @@ -81,22 +75,4 @@ static boolean hasItemOfClass(List<?> list, Class<?> clazz) {
.anyMatch(item -> item.getClass().equals(clazz));
}

@Configuration
static class RestTemplateProxyCustomizerConfiguration {

static final SimpleClientHttpRequestFactory FACTORY = createProxyFactory();

@Bean
public RestTemplateCustomizer proxyRestTemplateCustomizer() {
return (RestTemplate restTemplate) -> restTemplate.setRequestFactory(FACTORY);
}

static SimpleClientHttpRequestFactory createProxyFactory() {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080));
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setProxy(proxy);
return factory;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.aad.implementation;

import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.net.InetSocketAddress;
import java.net.Proxy;

@Configuration
public class RestTemplateProxyCustomizerConfiguration {

public static final SimpleClientHttpRequestFactory FACTORY = createProxyFactory();

@Bean
public RestTemplateCustomizer proxyRestTemplateCustomizer() {
return (RestTemplate restTemplate) -> restTemplate.setRequestFactory(FACTORY);
}

static SimpleClientHttpRequestFactory createProxyFactory() {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8080));
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setProxy(proxy);
return factory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

import com.azure.spring.cloud.autoconfigure.aad.AadClientRegistrationRepository;
import com.azure.spring.cloud.autoconfigure.aad.configuration.AadOAuth2ClientConfiguration;
import com.azure.spring.cloud.autoconfigure.aad.implementation.RestTemplateProxyCustomizerConfiguration;
import com.azure.spring.cloud.autoconfigure.aad.implementation.TestJwks;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapi.AadJwtBearerGrantRequestEntityConverter;
import com.azure.spring.cloud.autoconfigure.aad.implementation.webapp.AadAzureDelegatedOAuth2AuthorizedClientProvider;
import com.azure.spring.cloud.autoconfigure.aad.properties.AadAuthenticationProperties;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.RSAKey;
Expand All @@ -16,28 +18,39 @@
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.endpoint.DefaultJwtBearerTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.azure.spring.cloud.autoconfigure.aad.implementation.RestTemplateProxyCustomizerConfiguration.FACTORY;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.oauthClientAndResourceServerRunner;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.resourceServerContextRunner;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.resourceServerWithOboContextRunner;
import static com.azure.spring.cloud.autoconfigure.aad.implementation.WebApplicationContextRunnerUtils.webApplicationContextRunner;
import static com.azure.spring.cloud.core.implementation.util.ReflectionUtils.getField;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
Expand Down Expand Up @@ -223,6 +236,58 @@ void customConverterInJwtBearerOAuth2AuthorizedClientProviderWhenUsingPrivateKey
});
}

@Test
void restTemplateWellConfiguredForAllOAuth2AuthorizedClientProvidersWhenNotUsingPrivateKeyJwtMethod() {
resourceServerWithOboContextRunner()
.withUserConfiguration(AadOAuth2ClientConfiguration.class, RestTemplateProxyCustomizerConfiguration.class)
.run(context -> {
assertThat(context).doesNotHaveBean(OAuth2ClientAuthenticationJwkResolver.class);
assertRestTemplateWellConfiguredForAllOAuth2AuthorizedClientProviders(context);
});
}

@Test
void restTemplateWellConfiguredForAllOAuth2AuthorizedClientProvidersWhenUsingPrivateKeyJwtMethod() {
resourceServerWithOboContextRunner()
.withPropertyValues(
"spring.cloud.azure.active-directory.enabled=true",
"spring.cloud.azure.active-directory.credential.client-certificate-path=/test/test.pfx",
"spring.cloud.azure.active-directory.credential.client-certificate-password=test",
"spring.cloud.azure.active-directory.authorization-clients.graph.client-authentication-method=private_key_jwt",
"spring.cloud.azure.active-directory.authorization-clients.graph.scopes=https://graph.microsoft.com/User.Read",
"spring.cloud.azure.active-directory.authorization-clients.graph.authorization-grant-type=urn:ietf:params:oauth:grant-type:jwt-bearer",
"spring.cloud.azure.active-directory.authorization-clients.graph.scopes=api://52261059-e515-488e-84fd-a09a3f372814/File.Read"
)
.withUserConfiguration(AadOAuth2ClientConfiguration.class, RestTemplateProxyCustomizerConfiguration.class)
.run(context -> {
assertThat(context).hasSingleBean(OAuth2ClientAuthenticationJwkResolver.class);
assertRestTemplateWellConfiguredForAllOAuth2AuthorizedClientProviders(context);
});
}

private static void assertRestTemplateWellConfiguredForAllOAuth2AuthorizedClientProviders(ApplicationContext context) {
List<OAuth2AuthorizedClientProvider> providers = getAllOAuth2AuthorizedClientProviderThatShouldConfiguredRestTemplate(context);
assertEquals(4, providers.size());
providers.forEach(provider -> {
OAuth2AccessTokenResponseClient<?> client = (OAuth2AccessTokenResponseClient<?>) getField(provider.getClass(), "accessTokenResponseClient", provider);
RestTemplate restTemplate = (RestTemplate) getField(client.getClass(), "restOperations", client);
assertEquals(FACTORY, restTemplate.getRequestFactory());
});
}

@SuppressWarnings("unchecked")
private static List<OAuth2AuthorizedClientProvider> getAllOAuth2AuthorizedClientProviderThatShouldConfiguredRestTemplate(ApplicationContext context) {
final DefaultOAuth2AuthorizedClientManager manager = context.getBean(DefaultOAuth2AuthorizedClientManager.class);
DelegatingOAuth2AuthorizedClientProvider delegatingProvider =
(DelegatingOAuth2AuthorizedClientProvider) getField(DefaultOAuth2AuthorizedClientManager.class, "authorizedClientProvider", manager);
List<OAuth2AuthorizedClientProvider> providers =
(List<OAuth2AuthorizedClientProvider>) getField(DelegatingOAuth2AuthorizedClientProvider.class, "authorizedClientProviders", delegatingProvider);
return providers.stream()
.filter(provider -> !(provider instanceof AuthorizationCodeOAuth2AuthorizedClientProvider))
.filter(provider -> !(provider instanceof AadAzureDelegatedOAuth2AuthorizedClientProvider))
.collect(Collectors.toList());
}

@SuppressWarnings("unchecked")
private MultiValueMap<String, String> convertParameters(JwtBearerOAuth2AuthorizedClientProvider jwtBearerProvider,
ClientRegistrationRepository clientRepository) {
Expand Down
Loading