diff --git a/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfigurationTest.java b/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfigurationTest.java deleted file mode 100644 index 48187735a7b2..000000000000 --- a/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfigurationTest.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.azure.spring.aad.implementation; - -import com.azure.test.utils.AppRunner; -import org.junit.Test; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class AzureActiveDirectoryConfigurationTest { - - @Test - public void clientRegistered() { - try (AppRunner runner = createApp()) { - runner.start(); - - ClientRegistrationRepository repo = runner.getBean(ClientRegistrationRepository.class); - ClientRegistration azure = repo.findByRegistrationId("azure"); - - assertNotNull(azure); - assertEquals("fake-client-id", azure.getClientId()); - assertEquals("fake-client-secret", azure.getClientSecret()); - - AuthorizationServerEndpoints endpoints = new AuthorizationServerEndpoints(); - assertEquals(endpoints.authorizationEndpoint("fake-tenant-id"), - azure.getProviderDetails().getAuthorizationUri()); - assertEquals(endpoints.tokenEndpoint("fake-tenant-id"), azure.getProviderDetails().getTokenUri()); - assertEquals(endpoints.jwkSetEndpoint("fake-tenant-id"), azure.getProviderDetails().getJwkSetUri()); - assertEquals("{baseUrl}/login/oauth2/code/{registrationId}", azure.getRedirectUriTemplate()); - assertDefaultScopes( - azure, - "openid", - "profile", - "https://graph.microsoft.com/Directory.AccessAsUser.All", - "https://graph.microsoft.com/User.Read"); - } - } - - @Test - public void clientRequiresPermissionRegistered() { - try (AppRunner runner = createApp()) { - runner.property("azure.activedirectory.authorization.graph.scopes", "Calendars.Read"); - runner.start(); - - ClientRegistrationRepository repo = runner.getBean(ClientRegistrationRepository.class); - ClientRegistration azure = repo.findByRegistrationId("azure"); - ClientRegistration graph = repo.findByRegistrationId("graph"); - - assertNotNull(azure); - assertDefaultScopes( - azure, - "openid", - "profile", - "https://graph.microsoft.com/Directory.AccessAsUser.All", - "https://graph.microsoft.com/User.Read", - "offline_access", - "Calendars.Read"); - - assertNotNull(graph); - assertDefaultScopes(graph, "Calendars.Read"); - } - } - - @Test - public void clientRequiresMultiPermissions() { - try (AppRunner runner = createApp()) { - runner.property("azure.activedirectory.authorization.graph.scopes", "Calendars.Read"); - runner.property("azure.activedirectory.authorization.arm.scopes", "https://management.core.windows.net/user_impersonation"); - runner.start(); - - ClientRegistrationRepository repo = runner.getBean(ClientRegistrationRepository.class); - ClientRegistration azure = repo.findByRegistrationId("azure"); - ClientRegistration graph = repo.findByRegistrationId("graph"); - - assertNotNull(azure); - assertDefaultScopes( - azure, - "openid", - "profile", - "https://graph.microsoft.com/Directory.AccessAsUser.All", - "https://graph.microsoft.com/User.Read", - "offline_access", - "Calendars.Read", - "https://management.core.windows.net/user_impersonation"); - - assertNotNull(graph); - assertDefaultScopes(graph, "Calendars.Read"); - } - } - - @Test - public void clientRequiresPermissionInDefaultClient() { - try (AppRunner runner = createApp()) { - runner.property("azure.activedirectory.authorization.azure.scopes", "Calendars.Read"); - runner.start(); - - ClientRegistrationRepository repo = runner.getBean(ClientRegistrationRepository.class); - ClientRegistration azure = repo.findByRegistrationId("azure"); - - assertNotNull(azure); - assertDefaultScopes( - azure, - "openid", - "profile", - "https://graph.microsoft.com/Directory.AccessAsUser.All", - "https://graph.microsoft.com/User.Read", - "offline_access", - "Calendars.Read"); - } - } - - @Test - public void aadAwareClientRepository() { - try (AppRunner runner = createApp()) { - runner.property("azure.activedirectory.authorization.graph.scopes", "Calendars.Read"); - runner.start(); - - AzureClientRegistrationRepository repo = (AzureClientRegistrationRepository) runner.getBean(ClientRegistrationRepository.class); - ClientRegistration azure = repo.findByRegistrationId("azure"); - ClientRegistration graph = repo.findByRegistrationId("graph"); - - assertDefaultScopes( - repo.getAzureClient(), - "openid", "profile", "offline_access", "https://graph.microsoft.com/User.Read", - "https://graph.microsoft.com/Directory.AccessAsUser.All" - ); - assertEquals(repo.getAzureClient().getClient(), azure); - - assertFalse(repo.isAuthzClient(azure)); - assertTrue(repo.isAuthzClient(graph)); - assertFalse(repo.isAuthzClient("azure")); - assertTrue(repo.isAuthzClient("graph")); - - List clients = collectClients(repo); - assertEquals(1, clients.size()); - assertEquals("azure", clients.get(0).getRegistrationId()); - } - } - - @Test - public void defaultClientWithAuthzScope() { - try (AppRunner runner = createApp()) { - runner.property("azure.activedirectory.authorization.azure.scopes", "Calendars.Read"); - runner.start(); - - AzureClientRegistrationRepository repo = runner.getBean(AzureClientRegistrationRepository.class); - assertDefaultScopes( - repo.getAzureClient(), - "openid", "profile", "offline_access", "https://graph.microsoft.com/User.Read", - "https://graph.microsoft.com/Directory.AccessAsUser.All", "Calendars.Read" - ); - } - } - - @Test - public void customizeUri() { - try (AppRunner runner = createApp()) { - runner.property("azure.activedirectory.authorization-server-uri", "http://localhost/"); - runner.start(); - - AzureClientRegistrationRepository repo = runner.getBean(AzureClientRegistrationRepository.class); - ClientRegistration azure = repo.findByRegistrationId("azure"); - - AuthorizationServerEndpoints endpoints = new AuthorizationServerEndpoints("http://localhost/"); - assertEquals(endpoints.authorizationEndpoint("fake-tenant-id"), azure.getProviderDetails().getAuthorizationUri()); - assertEquals(endpoints.tokenEndpoint("fake-tenant-id"), azure.getProviderDetails().getTokenUri()); - assertEquals(endpoints.jwkSetEndpoint("fake-tenant-id"), azure.getProviderDetails().getJwkSetUri()); - } - } - - private AppRunner createApp() { - AppRunner result = new AppRunner(DumbApp.class); - result.property("azure.activedirectory.authorization-server-uri", "https://login.microsoftonline.com"); - result.property("azure.activedirectory.tenant-id", "fake-tenant-id"); - result.property("azure.activedirectory.client-id", "fake-client-id"); - result.property("azure.activedirectory.client-secret", "fake-client-secret"); - result.property("azure.activedirectory.user-group.allowed-groups", "groupA, groupB"); - return result; - } - - private void assertDefaultScopes(ClientRegistration client, String ... scopes) { - assertEquals(scopes.length, client.getScopes().size()); - for (String s : scopes) { - assertTrue(client.getScopes().contains(s)); - } - } - - private void assertDefaultScopes(AzureClientRegistration client, String ... expected) { - assertEquals(expected.length, client.getAccessTokenScopes().size()); - for (String e : expected) { - assertTrue(client.getAccessTokenScopes().contains(e)); - } - } - - private List collectClients(Iterable itr) { - List result = new ArrayList<>(); - itr.forEach(result::add); - return result; - } - - @Configuration - @EnableWebSecurity - @SpringBootApplication - public static class DumbApp { - } -} diff --git a/sdk/spring/azure-spring-boot-test-aad/src/test/resources/application.properties b/sdk/spring/azure-spring-boot-test-aad/src/test/resources/application.properties deleted file mode 100644 index 18650c6ce516..000000000000 --- a/sdk/spring/azure-spring-boot-test-aad/src/test/resources/application.properties +++ /dev/null @@ -1,8 +0,0 @@ -azure.activedirectory.tenant-id=fake-tenant-id -azure.activedirectory.client-id=fake-client-id -azure.activedirectory.client-secret=fake-client-secret -azure.activedirectory.user-group.allowed-groups=group1, group2 -# TODO: Delete the following content after "com.azure.spring.aad.implementation.AzureActiveDirectoryConfiguration" -# added in "sdk/spring/azure-spring-boot/src/main/resources/META-INF/spring.factories" -spring.security.oauth2.client.registration.azure.client-id=fake-client-id -spring.security.oauth2.client.registration.azure.client-secret=fake-client-secret \ No newline at end of file diff --git a/sdk/spring/azure-spring-boot-test-aad/src/test/resources/spring.factories b/sdk/spring/azure-spring-boot-test-aad/src/test/resources/spring.factories deleted file mode 100644 index 65c46c110a9d..000000000000 --- a/sdk/spring/azure-spring-boot-test-aad/src/test/resources/spring.factories +++ /dev/null @@ -1,3 +0,0 @@ -# TODO: Delete this file after "com.azure.spring.aad.implementation.AzureActiveDirectoryConfiguration" added in "sdk/spring/azure-spring-boot/src/main/resources/META-INF/spring.factories" -org.springframework.boot.env.EnvironmentPostProcessor=\ -com.azure.spring.aad.implementation.AzureActiveDirectoryConfiguration diff --git a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfiguration.java b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfiguration.java index 7add06b9bbf5..a546acacf024 100644 --- a/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfiguration.java +++ b/sdk/spring/azure-spring-boot/src/main/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfiguration.java @@ -5,12 +5,14 @@ import com.azure.spring.autoconfigure.aad.AADAuthenticationProperties; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; @@ -75,7 +77,6 @@ private Set allScopes() { private Set accessTokenScopes() { Set result = openidScopes(); if (properties.allowedGroupsConfigured()) { - result.add("https://graph.microsoft.com/Directory.AccessAsUser.All"); result.add("https://graph.microsoft.com/User.Read"); } addAzureConfiguredScopes(result); @@ -137,6 +138,7 @@ private ClientRegistration.Builder createClientBuilder(String id) { } @Configuration + @ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class) public static class DefaultAzureOAuth2Configuration extends AzureOAuth2Configuration { diff --git a/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AuthorizedClientRepoTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AuthorizedClientRepoTest.java similarity index 70% rename from sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AuthorizedClientRepoTest.java rename to sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AuthorizedClientRepoTest.java index ac54a56cd568..6219e0267854 100644 --- a/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AuthorizedClientRepoTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AuthorizedClientRepoTest.java @@ -1,14 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.spring.aad.implementation; -import com.azure.test.utils.AppRunner; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -17,6 +16,7 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.test.context.support.TestPropertySourceUtils; import java.time.Instant; import java.util.Optional; @@ -27,53 +27,46 @@ public class AuthorizedClientRepoTest { - private AppRunner runner; - private ClientRegistration azure; private ClientRegistration graph; - private OAuth2AuthorizedClientRepository repo; + private OAuth2AuthorizedClientRepository authorizedRepo; private MockHttpServletRequest request; private MockHttpServletResponse response; @BeforeEach public void setup() { - runner = createApp(); - runner.start(); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment( + context, + "azure.activedirectory.user-group.allowed-groups = group1, group2", + "azure.activedirectory.authorization-server-uri = fake-uri", + "azure.activedirectory.tenant-id = fake-tenant-id", + "azure.activedirectory.client-id = fake-client-id", + "azure.activedirectory.client-secret = fake-client-secret", + "azure.activedirectory.authorization.graph.scopes = Calendars.Read" + ); + context.register(AzureActiveDirectoryConfiguration.class); + context.refresh(); - AzureClientRegistrationRepository clientRepo = runner.getBean(AzureClientRegistrationRepository.class); + AzureClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); azure = clientRepo.findByRegistrationId("azure"); graph = clientRepo.findByRegistrationId("graph"); - repo = new AzureAuthorizedClientRepository(clientRepo); + authorizedRepo = new AzureAuthorizedClientRepository(clientRepo); request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); } - private AppRunner createApp() { - AppRunner result = new AppRunner(AzureActiveDirectoryConfigurationTest.DumbApp.class); - result.property("azure.activedirectory.authorization-server-uri", "fake-uri"); - result.property("azure.activedirectory.tenant-id", "fake-tenant-id"); - result.property("azure.activedirectory.client-id", "fake-client-id"); - result.property("azure.activedirectory.client-secret", "fake-client-secret"); - result.property("azure.activedirectory.authorization.graph.scopes", "Calendars.Read"); - return result; - } - - @AfterEach - public void tearDown() { - runner.stop(); - } - @Test public void loadInitAzureAuthzClient() { - repo.saveAuthorizedClient( + authorizedRepo.saveAuthorizedClient( createAuthorizedClient(azure), createAuthentication(), request, response); - OAuth2AuthorizedClient client = repo.loadAuthorizedClient( + OAuth2AuthorizedClient client = authorizedRepo.loadAuthorizedClient( "graph", createAuthentication(), request); @@ -88,13 +81,13 @@ public void loadInitAzureAuthzClient() { @Test public void saveAndLoadAzureAuthzClient() { - repo.saveAuthorizedClient( + authorizedRepo.saveAuthorizedClient( createAuthorizedClient(graph), createAuthentication(), request, response); - OAuth2AuthorizedClient client = repo.loadAuthorizedClient( + OAuth2AuthorizedClient client = authorizedRepo.loadAuthorizedClient( "graph", createAuthentication(), request); @@ -138,10 +131,4 @@ private boolean isTokenExpired(OAuth2AccessToken token) { .map(expiresAt -> expiresAt.isBefore(Instant.now())) .orElse(false); } - - @Configuration - @SpringBootApplication - @EnableWebSecurity - public static class DumbApp { - } } diff --git a/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AuthzCodeGrantRequestEntityConverterTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AuthzCodeGrantRequestEntityConverterTest.java similarity index 66% rename from sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AuthzCodeGrantRequestEntityConverterTest.java rename to sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AuthzCodeGrantRequestEntityConverterTest.java index b46b76e00d83..08b420d1f0a6 100644 --- a/sdk/spring/azure-spring-boot-test-aad/src/test/java/com/azure/spring/aad/implementation/AuthzCodeGrantRequestEntityConverterTest.java +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AuthzCodeGrantRequestEntityConverterTest.java @@ -1,19 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + package com.azure.spring.aad.implementation; -import com.azure.test.utils.AppRunner; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.RequestEntity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; +import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.util.MultiValueMap; import java.util.Optional; @@ -23,42 +23,35 @@ public class AuthzCodeGrantRequestEntityConverterTest { - private AppRunner runner; - private AzureClientRegistrationRepository repo; + private AzureClientRegistrationRepository clientRepo; private ClientRegistration azure; private ClientRegistration graph; @BeforeEach public void setupApp() { - runner = createApp(); - runner.start(); - - repo = runner.getBean(AzureClientRegistrationRepository.class); - azure = repo.findByRegistrationId("azure"); - graph = repo.findByRegistrationId("graph"); - } - - private AppRunner createApp() { - AppRunner result = new AppRunner(DumbApp.class); - result.property("azure.activedirectory.authorization-server-uri", "http://localhost"); - result.property("azure.activedirectory.tenant-id", "fake-tenant-id"); - result.property("azure.activedirectory.client-id", "fake-client-id"); - result.property("azure.activedirectory.client-secret", "fake-client-secret"); - result.property("azure.activedirectory.authorization.graph.scopes", "Calendars.Read"); - return result; - } + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment( + context, + "azure.activedirectory.authorization-server-uri = fake-uri", + "azure.activedirectory.authorization.graph.scopes = Calendars.Read", + "azure.activedirectory.client-id = fake-client-id", + "azure.activedirectory.client-secret = fake-client-secret", + "azure.activedirectory.tenant-id = fake-tenant-id", + "azure.activedirectory.user-group.allowed-groups = group1, group2" + ); + context.register(AzureActiveDirectoryConfiguration.class); + context.refresh(); - @AfterEach - public void tearDownApp() { - runner.stop(); + clientRepo = context.getBean(AzureClientRegistrationRepository.class); + azure = clientRepo.findByRegistrationId("azure"); + graph = clientRepo.findByRegistrationId("graph"); } @Test public void addScopeForDefaultClient() { MultiValueMap body = convertedBodyOf(createCodeGrantRequest(azure)); assertEquals( - "openid profile offline_access" - + " https://graph.microsoft.com/User.Read https://graph.microsoft.com/Directory.AccessAsUser.All", + "openid profile offline_access https://graph.microsoft.com/User.Read", body.getFirst("scope") ); } @@ -72,7 +65,7 @@ public void noScopeParamForOtherClient() { @SuppressWarnings("unchecked") private MultiValueMap convertedBodyOf(OAuth2AuthorizationCodeGrantRequest request) { AuthzCodeGrantRequestEntityConverter converter = - new AuthzCodeGrantRequestEntityConverter(repo.getAzureClient()); + new AuthzCodeGrantRequestEntityConverter(clientRepo.getAzureClient()); RequestEntity entity = converter.convert(request); return (MultiValueMap) Optional.ofNullable(entity) .map(HttpEntity::getBody) @@ -105,10 +98,4 @@ private OAuth2AuthorizationResponse createAuthorizationResponse() { builder.state("fake-state"); return builder.build(); } - - @Configuration - @SpringBootApplication - @EnableWebSecurity - public static class DumbApp { - } } diff --git a/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfigurationTest.java b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfigurationTest.java new file mode 100644 index 000000000000..5d8e696bab47 --- /dev/null +++ b/sdk/spring/azure-spring-boot/src/test/java/com/azure/spring/aad/implementation/AzureActiveDirectoryConfigurationTest.java @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.aad.implementation; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AzureActiveDirectoryConfigurationTest { + + private AnnotationConfigApplicationContext getContext() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment( + context, + "azure.activedirectory.client-id = fake-client-id", + "azure.activedirectory.client-secret = fake-client-secret", + "azure.activedirectory.tenant-id = fake-tenant-id", + "azure.activedirectory.user-group.allowed-groups = group1, group2" + ); + context.register(AzureActiveDirectoryConfiguration.class); + return context; + } + + @Test + public void clientRegistered() { + AnnotationConfigApplicationContext context = getContext(); + context.refresh(); + + ClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + ClientRegistration azure = clientRepo.findByRegistrationId("azure"); + + assertNotNull(azure); + assertEquals("fake-client-id", azure.getClientId()); + assertEquals("fake-client-secret", azure.getClientSecret()); + + AuthorizationServerEndpoints endpoints = new AuthorizationServerEndpoints(); + assertEquals(endpoints.authorizationEndpoint("fake-tenant-id"), + azure.getProviderDetails().getAuthorizationUri()); + assertEquals(endpoints.tokenEndpoint("fake-tenant-id"), azure.getProviderDetails().getTokenUri()); + assertEquals(endpoints.jwkSetEndpoint("fake-tenant-id"), azure.getProviderDetails().getJwkSetUri()); + assertEquals("{baseUrl}/login/oauth2/code/{registrationId}", azure.getRedirectUriTemplate()); + assertDefaultScopes(azure, "openid", "profile", "https://graph.microsoft.com/User.Read"); + } + + @Test + public void clientRequiresPermissionRegistered() { + AnnotationConfigApplicationContext context = getContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "azure.activedirectory.authorization.graph.scopes = Calendars.Read"); + context.refresh(); + + ClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + ClientRegistration azure = clientRepo.findByRegistrationId("azure"); + ClientRegistration graph = clientRepo.findByRegistrationId("graph"); + + assertNotNull(azure); + assertNotNull(graph); + assertDefaultScopes(azure, + "openid", "profile", "offline_access", "https://graph.microsoft.com/User.Read", "Calendars.Read"); + assertDefaultScopes(graph, "Calendars.Read"); + } + + @Test + public void clientRequiresMultiPermissions() { + AnnotationConfigApplicationContext context = getContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "azure.activedirectory.authorization.graph.scopes = Calendars.Read", + "azure.activedirectory.authorization.arm.scopes = https://management.core.windows.net/user_impersonation" + ); + context.refresh(); + + ClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + ClientRegistration azure = clientRepo.findByRegistrationId("azure"); + ClientRegistration graph = clientRepo.findByRegistrationId("graph"); + assertDefaultScopes( + azure, + "openid", + "profile", + "offline_access", + "Calendars.Read", + "https://graph.microsoft.com/User.Read", + "https://management.core.windows.net/user_impersonation"); + assertDefaultScopes(graph, "Calendars.Read"); + } + + @Test + public void clientRequiresPermissionInDefaultClient() { + AnnotationConfigApplicationContext context = getContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "azure.activedirectory.authorization.graph.scopes = Calendars.Read"); + context.refresh(); + + ClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + ClientRegistration azure = clientRepo.findByRegistrationId("azure"); + assertDefaultScopes(azure, + "openid", "profile", "offline_access", "https://graph.microsoft.com/User.Read", "Calendars.Read"); + } + + @Test + public void aadAwareClientRepository() { + AnnotationConfigApplicationContext context = getContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "azure.activedirectory.authorization.graph.scopes = Calendars.Read"); + context.refresh(); + + AzureClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + ClientRegistration azure = clientRepo.findByRegistrationId("azure"); + ClientRegistration graph = clientRepo.findByRegistrationId("graph"); + assertDefaultScopes( + clientRepo.getAzureClient(), + "openid", "profile", "offline_access", "https://graph.microsoft.com/User.Read" + ); + assertEquals(clientRepo.getAzureClient().getClient(), azure); + + assertFalse(clientRepo.isAuthzClient(azure)); + assertTrue(clientRepo.isAuthzClient(graph)); + assertFalse(clientRepo.isAuthzClient("azure")); + assertTrue(clientRepo.isAuthzClient("graph")); + + List clients = collectClients(clientRepo); + assertEquals(1, clients.size()); + assertEquals("azure", clients.get(0).getRegistrationId()); + } + + @Test + public void defaultClientWithAuthzScope() { + AnnotationConfigApplicationContext context = getContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "azure.activedirectory.authorization.azure.scopes = Calendars.Read"); + context.refresh(); + + AzureClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + assertDefaultScopes( + clientRepo.getAzureClient(), + "openid", "profile", "offline_access", "https://graph.microsoft.com/User.Read", "Calendars.Read" + ); + } + + @Test + public void customizeUri() { + AnnotationConfigApplicationContext context = getContext(); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, + "azure.activedirectory.authorization-server-uri = http://localhost/"); + context.refresh(); + + AzureClientRegistrationRepository clientRepo = context.getBean(AzureClientRegistrationRepository.class); + ClientRegistration azure = clientRepo.findByRegistrationId("azure"); + AuthorizationServerEndpoints endpoints = new AuthorizationServerEndpoints("http://localhost/"); + assertEquals(endpoints.authorizationEndpoint("fake-tenant-id"), + azure.getProviderDetails().getAuthorizationUri()); + assertEquals(endpoints.tokenEndpoint("fake-tenant-id"), azure.getProviderDetails().getTokenUri()); + assertEquals(endpoints.jwkSetEndpoint("fake-tenant-id"), azure.getProviderDetails().getJwkSetUri()); + } + + private void assertDefaultScopes(ClientRegistration client, String... scopes) { + assertEquals(scopes.length, client.getScopes().size()); + for (String s : scopes) { + assertTrue(client.getScopes().contains(s)); + } + } + + private void assertDefaultScopes(AzureClientRegistration client, String... expected) { + assertEquals(expected.length, client.getAccessTokenScopes().size()); + for (String e : expected) { + assertTrue(client.getAccessTokenScopes().contains(e)); + } + } + + private List collectClients(Iterable itr) { + List result = new ArrayList<>(); + itr.forEach(result::add); + return result; + } +}