From f45940eb15e8757d4a06a9412b7e1a3462a03dae Mon Sep 17 00:00:00 2001 From: Alexandre Dutra Date: Thu, 13 Jun 2024 00:02:31 +0200 Subject: [PATCH] OAuth2Client: integration tests for token exchange (#8810) --- .../auth/oauth2/ITOAuth2ClientKeycloak.java | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/api/client/src/intTest/java/org/projectnessie/client/auth/oauth2/ITOAuth2ClientKeycloak.java b/api/client/src/intTest/java/org/projectnessie/client/auth/oauth2/ITOAuth2ClientKeycloak.java index c966683363f..2cb5ef4f374 100644 --- a/api/client/src/intTest/java/org/projectnessie/client/auth/oauth2/ITOAuth2ClientKeycloak.java +++ b/api/client/src/intTest/java/org/projectnessie/client/auth/oauth2/ITOAuth2ClientKeycloak.java @@ -374,6 +374,129 @@ void testOAuth2ClientExpiredToken() { } } + /** + * Tests a simple delegation scenario with a fixed subject token and the client playing the role + * of the actor. + */ + @Test + void testOAuth2ClientTokenExchangeDelegation1() { + AccessToken subjectToken; + try (OAuth2Client subjectClient = + new OAuth2Client(clientConfig("Private1", true, true).grantType(PASSWORD).build())) { + subjectToken = subjectClient.fetchNewTokens().getAccessToken(); + } + TokenExchangeConfig tokenExchangeConfig = + TokenExchangeConfig.builder() + .enabled(true) + .subjectToken(TypedToken.fromAccessToken(subjectToken)) + .actorTokenProvider( + (accessToken, refreshToken) -> TypedToken.fromAccessToken(accessToken)) + .build(); + try (OAuth2Client client = + new OAuth2Client( + clientConfig("Private2", true, true) + .tokenExchangeConfig(tokenExchangeConfig) + .build())) { + Tokens tokens = client.fetchNewTokens(); + soft.assertThat(tokens.getAccessToken()).isNotNull(); + } + } + + /** + * Tests a simple delegation scenario with a fixed actor token and the client playing the role of + * the subject. + */ + @Test + void testOAuth2ClientTokenExchangeDelegation2() { + AccessToken actorToken; + try (OAuth2Client client = + new OAuth2Client(clientConfig("Private1", true, true).grantType(PASSWORD).build())) { + actorToken = client.fetchNewTokens().getAccessToken(); + } + TokenExchangeConfig tokenExchangeConfig = + TokenExchangeConfig.builder() + .enabled(true) + .subjectTokenProvider( + (accessToken, refreshToken) -> TypedToken.fromAccessToken(accessToken)) + .actorToken(TypedToken.fromAccessToken(actorToken)) + .build(); + try (OAuth2Client client = + new OAuth2Client( + clientConfig("Private2", true, true) + .tokenExchangeConfig(tokenExchangeConfig) + .build())) { + Tokens tokens = client.fetchNewTokens(); + soft.assertThat(tokens.getAccessToken()).isNotNull(); + } + } + + /** + * Tests a simple delegation scenario with the client playing both the role of the subject and the + * actor. + */ + @Test + void testOAuth2ClientTokenExchangeDelegation3() { + TokenExchangeConfig tokenExchangeConfig = + TokenExchangeConfig.builder() + .enabled(true) + .subjectTokenProvider( + (accessToken, refreshToken) -> TypedToken.fromAccessToken(accessToken)) + .actorTokenProvider( + (accessToken, refreshToken) -> TypedToken.fromAccessToken(accessToken)) + .build(); + try (OAuth2Client client = + new OAuth2Client( + clientConfig("Private1", true, true) + .tokenExchangeConfig(tokenExchangeConfig) + .build())) { + Tokens tokens = client.fetchNewTokens(); + soft.assertThat(tokens.getAccessToken()).isNotNull(); + } + } + + /** + * Tests a simple impersonation scenario with a fixed subject token (and no actor token). The + * client discards its own token. + */ + @Test + void testOAuth2ClientTokenExchangeImpersonation1() { + AccessToken subjectToken; + try (OAuth2Client subjectClient = + new OAuth2Client(clientConfig("Private1", true, true).grantType(PASSWORD).build())) { + subjectToken = subjectClient.fetchNewTokens().getAccessToken(); + } + TokenExchangeConfig tokenExchangeConfig = + TokenExchangeConfig.builder() + .enabled(true) + .subjectToken(TypedToken.fromAccessToken(subjectToken)) + .build(); + try (OAuth2Client client = + new OAuth2Client( + clientConfig("Private2", true, true) + .tokenExchangeConfig(tokenExchangeConfig) + .build())) { + Tokens tokens = client.fetchNewTokens(); + soft.assertThat(tokens.getAccessToken()).isNotNull(); + } + } + + /** + * Tests a simple impersonation scenario with the client using its own token as the subject token, + * and no actor token. The client swaps its token for another one, roughly equivalent. + */ + @Test + void testOAuth2ClientTokenExchangeImpersonation2() { + TokenExchangeConfig tokenExchangeConfig = TokenExchangeConfig.builder().enabled(true).build(); + try (OAuth2Client client = + new OAuth2Client( + clientConfig("Private2", true, true) + .tokenExchangeConfig(tokenExchangeConfig) + .build())) { + Tokens tokens = client.fetchNewTokens(); + soft.assertThat(tokens.getAccessToken()).isNotNull(); + } + } + /** * Attempts to use the access token to obtain a UMA (User Management Access) ticket from Keycloak, * authorizing the client represented by the token to access resources hosted by "ResourceServer".