Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement aad authn and authz #17325

Merged
merged 12 commits into from
Nov 11, 2020
9 changes: 9 additions & 0 deletions sdk/spring/azure-spring-boot-test-aad/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package com.azure.test.aad.auth;

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

import com.azure.spring.aad.implementation.AzureClientRegistrationRepository;
import com.azure.spring.aad.implementation.DefaultClient;
import com.azure.spring.aad.implementation.IdentityEndpoints;
import com.azure.test.utils.AppRunner;
import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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 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 AppAutoConfigTest {

@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());

IdentityEndpoints endpoints = new IdentityEndpoints();
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");
}
}

@Test
public void clientRequiresPermissionRegistered() {
try (AppRunner runner = createApp()) {
runner.property("azure.activedirectory.authorization.graph.scope", "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", "offline_access", "Calendars.Read");

assertNotNull(graph);
assertDefaultScopes(graph, "Calendars.Read");
}
}

@Test
public void clientRequiresMultiPermissions() {
try (AppRunner runner = createApp()) {
runner.property("azure.activedirectory.authorization.graph.scope", "Calendars.Read");
runner.property("azure.activedirectory.authorization.arm.scope", "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",
"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.scope", "Calendars.Read");
runner.start();

ClientRegistrationRepository repo = runner.getBean(ClientRegistrationRepository.class);
ClientRegistration azure = repo.findByRegistrationId("azure");

assertNotNull(azure);
assertDefaultScopes(azure, "openid", "profile", "offline_access", "Calendars.Read");
}
}

@Test
public void aadAwareClientRepository() {
try (AppRunner runner = createApp()) {
runner.property("azure.activedirectory.authorization.graph.scope", "Calendars.Read");
runner.start();

AzureClientRegistrationRepository repo = (AzureClientRegistrationRepository) runner.getBean(ClientRegistrationRepository.class);
ClientRegistration azure = repo.findByRegistrationId("azure");
ClientRegistration graph = repo.findByRegistrationId("graph");

assertDefaultScopes(repo.defaultClient(), "openid", "profile", "offline_access");
assertEquals(repo.defaultClient().client(), azure);

assertFalse(repo.isAuthzClient(azure));
assertTrue(repo.isAuthzClient(graph));
assertFalse(repo.isAuthzClient("azure"));
assertTrue(repo.isAuthzClient("graph"));

List<ClientRegistration> clients = collectClients((Iterable<ClientRegistration>) 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.scope", "Calendars.Read");
runner.start();

AzureClientRegistrationRepository repo = runner.getBean(AzureClientRegistrationRepository.class);
assertDefaultScopes(repo.defaultClient(), "openid", "profile", "offline_access", "Calendars.Read");
}
}

@Test
public void customizeUri() {
try (AppRunner runner = createApp()) {
runner.property("azure.activedirectory.uri", "http://localhost/");
runner.start();

AzureClientRegistrationRepository repo = runner.getBean(AzureClientRegistrationRepository.class);
ClientRegistration azure = repo.findByRegistrationId("azure");

IdentityEndpoints endpoints = new IdentityEndpoints("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.tenant-id", "fake-tenant-id");
result.property("azure.activedirectory.client-id", "fake-client-id");
result.property("azure.activedirectory.client-secret", "fake-client-secret");
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(DefaultClient client, String ... expected) {
assertEquals(expected.length, client.scopes().size());
for (String e : expected) {
assertTrue(client.scopes().contains(e));
}
}

private List<ClientRegistration> collectClients(Iterable<ClientRegistration> itr) {
List<ClientRegistration> result = new ArrayList<>();
itr.forEach(c -> result.add(c));
return result;
}

@Configuration
@EnableAutoConfiguration
@EnableWebSecurity
public static class DumbApp {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.azure.test.aad.auth;

import java.time.Instant;

import com.azure.spring.aad.implementation.AzureAuthorizedClientRepository;
import com.azure.spring.aad.implementation.AzureClientRegistrationRepository;
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.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class AuthorizedClientRepoTest {

private AppRunner runner;

private ClientRegistration azure;
private ClientRegistration graph;

private OAuth2AuthorizedClientRepository repo;
private MockHttpServletRequest request;
private MockHttpServletResponse response;

@BeforeEach
public void setup() {
runner = createApp();
runner.start();

AzureClientRegistrationRepository clientRepo = runner.getBean(AzureClientRegistrationRepository.class);
azure = clientRepo.findByRegistrationId("azure");
graph = clientRepo.findByRegistrationId("graph");

repo = new AzureAuthorizedClientRepository(clientRepo);
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}

private AppRunner createApp() {
AppRunner result = new AppRunner(AppAutoConfigTest.DumbApp.class);
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.scope", "Calendars.Read");
return result;
}

@AfterEach
public void tearDown() {
runner.stop();
}

@Test
public void loadInitAzureAuthzClient() {
repo.saveAuthorizedClient(
createAuthorizedClient(azure),
createAuthentication(),
request,
response);

OAuth2AuthorizedClient client = repo.loadAuthorizedClient(
"graph",
createAuthentication(),
request);

assertNotNull(client);
assertNotNull(client.getAccessToken());
assertNotNull(client.getRefreshToken());

assertTrue(isTokenExpired(client.getAccessToken()));
assertEquals("fake-refresh-token", client.getRefreshToken().getTokenValue());
}

@Test
public void saveAndLoadAzureAuthzClient() {
repo.saveAuthorizedClient(
createAuthorizedClient(graph),
createAuthentication(),
request,
response);

OAuth2AuthorizedClient client = repo.loadAuthorizedClient(
"graph",
createAuthentication(),
request);

assertNotNull(client);
assertNotNull(client.getAccessToken());
assertNotNull(client.getRefreshToken());

assertEquals("fake-access-token", client.getAccessToken().getTokenValue());
assertEquals("fake-refresh-token", client.getRefreshToken().getTokenValue());
}

private OAuth2AuthorizedClient createAuthorizedClient(ClientRegistration client) {
OAuth2AuthorizedClient result = new OAuth2AuthorizedClient(
client,
"fake-principal-name",
createAccessToken(),
createRefreshToken());

return result;
}

private OAuth2AccessToken createAccessToken() {
return new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER,
"fake-access-token",
Instant.MIN,
Instant.MAX
);
}

private OAuth2RefreshToken createRefreshToken() {
return new OAuth2RefreshToken("fake-refresh-token", Instant.MIN);
}

private Authentication createAuthentication() {
return new PreAuthenticatedAuthenticationToken("fake-user", "fake-crednetial");
}

private boolean isTokenExpired(OAuth2AccessToken token) {
return token.getExpiresAt().isBefore(Instant.now());
}

@Configuration
@EnableAutoConfiguration
@EnableWebSecurity
public static class DumbApp {}
}
Loading