From dc140ba03a6aba0ade532a60f4aeda1e44e53e22 Mon Sep 17 00:00:00 2001 From: Marko Strukelj Date: Wed, 28 Feb 2024 19:09:25 +0100 Subject: [PATCH] Username extraction bug fixes and additions - Added `oauth.username.prefix` - Fixed `oauth.fallback.username.prefix` ignored - a bug introduced in 0.13.0 Signed-off-by: Marko Strukelj --- README.md | 4 +++ .../JaasClientOauthLoginCallbackHandler.java | 5 ++-- .../io/strimzi/kafka/oauth/common/Config.java | 5 +++- .../oauth/common/PrincipalExtractor.java | 29 +++++++++++++++---- .../oauth/common/PrincipalExtractorTest.java | 16 +++++----- .../validator/PrincipalExtractorTest.java | 26 ++++++++++++----- ...asServerOauthValidatorCallbackHandler.java | 8 +++-- .../testsuite/oauth/auth/BasicTests.java | 4 +-- .../testsuite/oauth/MockOAuthTests.java | 10 +++---- .../oauth/mockoauth/JWKSKeyUseTest.java | 2 +- .../oauth/mockoauth/JaasServerConfigTest.java | 4 +-- ...sswordAuthAndPrincipalExtractionTest.java} | 22 ++++++++++++-- 12 files changed, 96 insertions(+), 39 deletions(-) rename testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/{PasswordAuthTest.java => PasswordAuthAndPrincipalExtractionTest.java} (84%) diff --git a/README.md b/README.md index 285205e4..8b253d30 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,10 @@ Use `oauth.username.claim` to map the claim (attribute) where the value you want If `oauth.username.claim` is specified the value of that claim is used instead, but if not set, the automatic fallback claim is the `sub` claim. +You can specify a prefix that is automatically prepended to the user id. This allows for the consistent mapping of user ids into the same name space and may be needed to prevent name collisions. +One use case is to assign a different prefix for each server when using different authorization servers for different listeners: +- `oauth.username.prefix` (e.g.: "internal_" - there is no prefix set by default) + You can specify the secondary claim to fall back to, which allows you to map multiple account types into the same principal namespace: - `oauth.fallback.username.claim` (e.g.: "client_id", for nested attributes use `[topAttrKey].[subAttrKey]`. Claim names can also be single quoted: `['topAttrKey'].['subAttrKey']`) - `oauth.fallback.username.prefix` (e.g.: "client-account-") diff --git a/oauth-client/src/main/java/io/strimzi/kafka/oauth/client/JaasClientOauthLoginCallbackHandler.java b/oauth-client/src/main/java/io/strimzi/kafka/oauth/client/JaasClientOauthLoginCallbackHandler.java index 0467960e..814554fd 100644 --- a/oauth-client/src/main/java/io/strimzi/kafka/oauth/client/JaasClientOauthLoginCallbackHandler.java +++ b/oauth-client/src/main/java/io/strimzi/kafka/oauth/client/JaasClientOauthLoginCallbackHandler.java @@ -149,13 +149,14 @@ public void configure(Map configs, String saslMechanism, List * If not, it checks if env variable with name equal to key exists. - * + *

* Ultimately, it checks the defaults passed at Config object construction time. *

* If no configuration is found for key, it returns the fallback value. diff --git a/oauth-common/src/main/java/io/strimzi/kafka/oauth/common/PrincipalExtractor.java b/oauth-common/src/main/java/io/strimzi/kafka/oauth/common/PrincipalExtractor.java index fa575779..a0c9dcce 100644 --- a/oauth-common/src/main/java/io/strimzi/kafka/oauth/common/PrincipalExtractor.java +++ b/oauth-common/src/main/java/io/strimzi/kafka/oauth/common/PrincipalExtractor.java @@ -13,7 +13,8 @@ * An object with logic for extracting a principal name (i.e. a user id) from a JWT token. *

* First a claim configured as usernameClaim is looked up. - * If not found the claim configured as fallbackUsernameClaim is looked up. If that one is found and if + * If found, and the usernamePrefix is configured, it is prepended to the value of the claim. + * If not found, the claim configured as fallbackUsernameClaim is looked up. If that one is found and if * the fallbackUsernamePrefix is configured prefix the found value with the prefix, otherwise not. *

* The claim specification uses the following rules: @@ -44,6 +45,7 @@ public class PrincipalExtractor { private final Extractor usernameExtractor; + private final String usernamePrefix; private final Extractor fallbackUsernameExtractor; private final String fallbackUsernamePrefix; @@ -52,6 +54,7 @@ public class PrincipalExtractor { */ public PrincipalExtractor() { usernameExtractor = null; + usernamePrefix = null; fallbackUsernameExtractor = null; fallbackUsernamePrefix = null; } @@ -60,11 +63,25 @@ public PrincipalExtractor() { * Create a new instance * * @param usernameClaim Attribute name for an attribute containing the user id to lookup first. + */ + public PrincipalExtractor(String usernameClaim) { + this.usernameExtractor = parseClaimSpec(usernameClaim); + usernamePrefix = null; + fallbackUsernameExtractor = null; + fallbackUsernamePrefix = null; + } + + /** + * Create a new instance + * + * @param usernameClaim Attribute name for an attribute containing the user id to lookup first. + * @param usernamePrefix A prefix to prepend to the user id * @param fallbackUsernameClaim Attribute name for an attribute containg the user id to lookup as a fallback * @param fallbackUsernamePrefix A prefix to prepend to the value of the fallback attribute value if set */ - public PrincipalExtractor(String usernameClaim, String fallbackUsernameClaim, String fallbackUsernamePrefix) { + public PrincipalExtractor(String usernameClaim, String usernamePrefix, String fallbackUsernameClaim, String fallbackUsernamePrefix) { this.usernameExtractor = parseClaimSpec(usernameClaim); + this.usernamePrefix = usernamePrefix; this.fallbackUsernameExtractor = parseClaimSpec(fallbackUsernameClaim); this.fallbackUsernamePrefix = fallbackUsernamePrefix; } @@ -81,11 +98,11 @@ public String getPrincipal(JsonNode json) { if (usernameExtractor != null) { result = extractUsername(usernameExtractor, json); if (result != null) { - return result; + return usernamePrefix != null ? usernamePrefix + result : result; } if (fallbackUsernameExtractor != null) { result = extractUsername(fallbackUsernameExtractor, json); - return result; + return result != null && fallbackUsernamePrefix != null ? fallbackUsernamePrefix + result : result; } } @@ -120,7 +137,7 @@ public String getSub(JsonNode json) { @Override public String toString() { - return "PrincipalExtractor {usernameClaim: " + usernameExtractor + ", fallbackUsernameClaim: " + fallbackUsernameExtractor + ", fallbackUsernamePrefix: " + fallbackUsernamePrefix + "}"; + return "PrincipalExtractor {usernameClaim: " + usernameExtractor + ", usernamePrefix: " + usernamePrefix + ", fallbackUsernameClaim: " + fallbackUsernameExtractor + ", fallbackUsernamePrefix: " + fallbackUsernamePrefix + "}"; } /** @@ -129,7 +146,7 @@ public String toString() { * @return True if any of the constructor parameters is set */ public boolean isConfigured() { - return usernameExtractor != null || fallbackUsernameExtractor != null || fallbackUsernamePrefix != null; + return usernameExtractor != null || usernamePrefix != null || fallbackUsernameExtractor != null || fallbackUsernamePrefix != null; } /** diff --git a/oauth-common/src/test/java/io/strimzi/kafka/oauth/common/PrincipalExtractorTest.java b/oauth-common/src/test/java/io/strimzi/kafka/oauth/common/PrincipalExtractorTest.java index 083cfbbd..0e4d4ee9 100644 --- a/oauth-common/src/test/java/io/strimzi/kafka/oauth/common/PrincipalExtractorTest.java +++ b/oauth-common/src/test/java/io/strimzi/kafka/oauth/common/PrincipalExtractorTest.java @@ -12,16 +12,16 @@ public class PrincipalExtractorTest { @Test public void testToStringMethod() { - PrincipalExtractor extractor = new PrincipalExtractor("username.claim", null, null); - Assert.assertEquals("PrincipalExtractor {usernameClaim: username.claim, fallbackUsernameClaim: null, fallbackUsernamePrefix: null}", extractor.toString()); + PrincipalExtractor extractor = new PrincipalExtractor("username.claim", null, null, null); + Assert.assertEquals("PrincipalExtractor {usernameClaim: username.claim, usernamePrefix: null, fallbackUsernameClaim: null, fallbackUsernamePrefix: null}", extractor.toString()); - extractor = new PrincipalExtractor("['username'].['claim']", null, null); - Assert.assertEquals("PrincipalExtractor {usernameClaim: ['username'].['claim'], fallbackUsernameClaim: null, fallbackUsernamePrefix: null}", extractor.toString()); + extractor = new PrincipalExtractor("['username'].['claim']", "admins_", null, "client_"); + Assert.assertEquals("PrincipalExtractor {usernameClaim: ['username'].['claim'], usernamePrefix: admins_, fallbackUsernameClaim: null, fallbackUsernamePrefix: client_}", extractor.toString()); - extractor = new PrincipalExtractor("username", "user.id", null); - Assert.assertEquals("PrincipalExtractor {usernameClaim: username, fallbackUsernameClaim: user.id, fallbackUsernamePrefix: null}", extractor.toString()); + extractor = new PrincipalExtractor("username", null, "user.id", null); + Assert.assertEquals("PrincipalExtractor {usernameClaim: username, usernamePrefix: null, fallbackUsernameClaim: user.id, fallbackUsernamePrefix: null}", extractor.toString()); - extractor = new PrincipalExtractor("username", "['user.id']", null); - Assert.assertEquals("PrincipalExtractor {usernameClaim: username, fallbackUsernameClaim: ['user.id'], fallbackUsernamePrefix: null}", extractor.toString()); + extractor = new PrincipalExtractor("username", "intra_", "['user.id']", "intra_service-account-"); + Assert.assertEquals("PrincipalExtractor {usernameClaim: username, usernamePrefix: intra_, fallbackUsernameClaim: ['user.id'], fallbackUsernamePrefix: intra_service-account-}", extractor.toString()); } } diff --git a/oauth-common/src/test/java/io/strimzi/kafka/oauth/validator/PrincipalExtractorTest.java b/oauth-common/src/test/java/io/strimzi/kafka/oauth/validator/PrincipalExtractorTest.java index 1cf5032b..62a7e808 100644 --- a/oauth-common/src/test/java/io/strimzi/kafka/oauth/validator/PrincipalExtractorTest.java +++ b/oauth-common/src/test/java/io/strimzi/kafka/oauth/validator/PrincipalExtractorTest.java @@ -115,25 +115,31 @@ public void testUsernameClaim() throws IOException { "[ 'userInfo' ] [ 'id' ]" }; + String usernamePrefix = "prefix-"; + String fallbackUsernamePrefix = "fallback_prefix-"; + for (int i = 0; i < claimSpec.length; i++) { String query = claimSpec[i]; String expected = claimSpec[++i]; try { - PrincipalExtractor extractor = new PrincipalExtractor(query, null, null); - Assert.assertEquals(query + " top level works as primary", expected, extractor.getPrincipal(json)); + PrincipalExtractor extractor = new PrincipalExtractor(query, usernamePrefix, null, null); + Assert.assertEquals(query + " top level works as primary", applyPrefix(usernamePrefix, expected), extractor.getPrincipal(json)); + } catch (Exception e) { + throw new RuntimeException("Unexpected error while testing: " + query + " expecting it to return: " + applyPrefix(usernamePrefix, expected), e); + } - extractor = new PrincipalExtractor("nonexisting", query, null); - Assert.assertEquals(query + " top level works as fallback", expected, extractor.getPrincipal(json)); + try { + PrincipalExtractor extractor = new PrincipalExtractor("nonexisting", usernamePrefix, query, fallbackUsernamePrefix); + Assert.assertEquals(query + " top level works as fallback", applyPrefix(fallbackUsernamePrefix, expected), extractor.getPrincipal(json)); } catch (Exception e) { - throw new RuntimeException("Unexpected error while testing: " + query + " expecting it to return: " + expected, e); + throw new RuntimeException("Unexpected error while testing: " + query + " expecting it to return: " + applyPrefix(fallbackUsernamePrefix, expected), e); } } - for (int i = 0; i < claimSpecError.length; i++) { - String query = claimSpecError[i]; + for (String query : claimSpecError) { try { - PrincipalExtractor extractor = new PrincipalExtractor(query, "nonexisting", null); + PrincipalExtractor extractor = new PrincipalExtractor(query, usernamePrefix, "nonexisting", fallbackUsernamePrefix); extractor.getPrincipal(json); Assert.fail("Should have failed"); } catch (JsonPathQueryException e) { @@ -142,4 +148,8 @@ public void testUsernameClaim() throws IOException { } } } + + private String applyPrefix(String prefix, String value) { + return value != null ? prefix + value : null; + } } diff --git a/oauth-server/src/main/java/io/strimzi/kafka/oauth/server/JaasServerOauthValidatorCallbackHandler.java b/oauth-server/src/main/java/io/strimzi/kafka/oauth/server/JaasServerOauthValidatorCallbackHandler.java index 71176cf4..4ff3d4a4 100644 --- a/oauth-server/src/main/java/io/strimzi/kafka/oauth/server/JaasServerOauthValidatorCallbackHandler.java +++ b/oauth-server/src/main/java/io/strimzi/kafka/oauth/server/JaasServerOauthValidatorCallbackHandler.java @@ -144,10 +144,13 @@ *

  • oauth.username.claim The attribute key that should be used to extract the user id. If not set `sub` attribute is used.
    * The attribute key refers to the JWT token claim when fast local validation is used, or to attribute in the response by introspection endpoint when introspection based validation is used. * For nested attributes use '[topAttrKey].[subAttrKey]'. Claim names can also be single quoted: ['topAttrKey'].['subAttrKey']. It has no default value.
  • + *
  • oauth.username.prefix The prefix to automatically prepend to the user id.
    + * This allows for consistent mapping of user ids into the same name space and may be needed to prevent name collisions.
    + * One use case is to assign a different prefix for each server when using different authorization servers for different listeners. By default there is no prefix.
  • *
  • oauth.fallback.username.claim The fallback username claim to be used for the user id if the attribute key specified by `oauth.username.claim`
    * is not present. This is useful when `client_credentials` authentication only results in the client id being provided in another claim. For nested attributes same rules apply as for `oauth.username.claim`. *
  • oauth.fallback.username.prefix The prefix to use with the value of oauth.fallback.username.claim to construct the user id.
    - * This only takes effect if oauth.fallback.username.claim is true, and the value is present for the claim. + * This only takes effect if oauth.fallback.username.claim is configured, and the value is present for the claim. * Mapping usernames and client ids into the same user id space is useful in preventing name collisions.
  • *
  • oauth.check.issuer Enable or disable issuer checking.
    * By default issuer is checked using the value configured by oauth.valid.issuer.uri. Default value is true
  • @@ -266,12 +269,13 @@ public void delegatedConfigure(Map configs, String saslMechanism, Lis includeAcceptHeader = config.getValueAsBoolean(Config.OAUTH_INCLUDE_ACCEPT_HEADER, true); String usernameClaim = config.getValue(Config.OAUTH_USERNAME_CLAIM); + String usernamePrefix = config.getValue(Config.OAUTH_USERNAME_PREFIX); String fallbackUsernameClaim = config.getValue(Config.OAUTH_FALLBACK_USERNAME_CLAIM); String fallbackUsernamePrefix = config.getValue(Config.OAUTH_FALLBACK_USERNAME_PREFIX); validateFallbackUsernameParameters(usernameClaim, fallbackUsernameClaim, fallbackUsernamePrefix); - principalExtractor = new PrincipalExtractor(usernameClaim, fallbackUsernameClaim, fallbackUsernamePrefix); + principalExtractor = new PrincipalExtractor(usernameClaim, usernamePrefix, fallbackUsernameClaim, fallbackUsernamePrefix); String clientId = config.getValue(Config.OAUTH_CLIENT_ID); String clientSecret = config.getValue(Config.OAUTH_CLIENT_SECRET); diff --git a/testsuite/keycloak-auth-tests/src/test/java/io/strimzi/testsuite/oauth/auth/BasicTests.java b/testsuite/keycloak-auth-tests/src/test/java/io/strimzi/testsuite/oauth/auth/BasicTests.java index 7c294518..6f7619d5 100644 --- a/testsuite/keycloak-auth-tests/src/test/java/io/strimzi/testsuite/oauth/auth/BasicTests.java +++ b/testsuite/keycloak-auth-tests/src/test/java/io/strimzi/testsuite/oauth/auth/BasicTests.java @@ -436,7 +436,7 @@ void passwordGrantWithIntrospection() throws Exception { String accessToken = loginWithUsernamePassword(URI.create(tokenEndpointUri), username, password, clientId); TokenInfo tokenInfo = introspectAccessToken(accessToken, - new PrincipalExtractor("preferred_username", null, null)); + new PrincipalExtractor("preferred_username")); Assert.assertEquals("Token contains 'preferred_username' claim with value equal to username", username, tokenInfo.principal()); @@ -465,7 +465,7 @@ void passwordGrantWithIntrospection() throws Exception { accessToken = loginWithUsernamePassword(URI.create(tokenEndpointUri), username, password, confidentialClientId, confidentialClientSecret); tokenInfo = introspectAccessToken(accessToken, - new PrincipalExtractor("preferred_username", null, null)); + new PrincipalExtractor("preferred_username")); Assert.assertEquals("Token contains 'preferred_username' claim with value equal to username", username, tokenInfo.principal()); diff --git a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/MockOAuthTests.java b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/MockOAuthTests.java index 2bf4b5ee..95cc27b2 100644 --- a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/MockOAuthTests.java +++ b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/MockOAuthTests.java @@ -8,17 +8,17 @@ import io.strimzi.testsuite.oauth.common.TestContainersLogCollector; import io.strimzi.testsuite.oauth.common.TestContainersWatcher; import io.strimzi.testsuite.oauth.mockoauth.AuthorizationEndpointsTest; -import io.strimzi.testsuite.oauth.mockoauth.JaasServerConfigTest; -import io.strimzi.testsuite.oauth.mockoauth.metrics.MetricsTest; import io.strimzi.testsuite.oauth.mockoauth.ClientAssertionAuthTest; import io.strimzi.testsuite.oauth.mockoauth.ConnectTimeoutTests; import io.strimzi.testsuite.oauth.mockoauth.JWKSKeyUseTest; import io.strimzi.testsuite.oauth.mockoauth.JaasClientConfigTest; +import io.strimzi.testsuite.oauth.mockoauth.JaasServerConfigTest; import io.strimzi.testsuite.oauth.mockoauth.KeycloakAuthorizerTest; -import io.strimzi.testsuite.oauth.mockoauth.PasswordAuthTest; +import io.strimzi.testsuite.oauth.mockoauth.PasswordAuthAndPrincipalExtractionTest; import io.strimzi.testsuite.oauth.mockoauth.RetriesTests; import io.strimzi.testsuite.oauth.mockoauth.KerberosListenerTest; +import io.strimzi.testsuite.oauth.mockoauth.metrics.MetricsTest; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; @@ -96,8 +96,8 @@ public void runTests() throws Exception { logStart("JaasServerConfigTest :: Server Configuration Tests"); new JaasServerConfigTest().doTest(); - logStart("PasswordAuthTest :: Password Grant Tests"); - new PasswordAuthTest().doTest(); + logStart("PasswordAuthAndPrincipalExtractionTest :: Password Grant + Fallback Username / Prefix Tests"); + new PasswordAuthAndPrincipalExtractionTest().doTest(); logStart("ConnectTimeoutTests :: HTTP Timeout Tests"); new ConnectTimeoutTests(kafkaContainer).doTest(); diff --git a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JWKSKeyUseTest.java b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JWKSKeyUseTest.java index 1e34b8f5..0f46e738 100644 --- a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JWKSKeyUseTest.java +++ b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JWKSKeyUseTest.java @@ -25,7 +25,7 @@ import static io.strimzi.testsuite.oauth.mockoauth.Common.getProjectRoot; public class JWKSKeyUseTest { - private static final Logger log = LoggerFactory.getLogger(PasswordAuthTest.class); + private static final Logger log = LoggerFactory.getLogger(JWKSKeyUseTest.class); public void doTest() throws Exception { Services.configure(Collections.emptyMap()); diff --git a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JaasServerConfigTest.java b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JaasServerConfigTest.java index 7e77c6c8..92b2bef0 100644 --- a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JaasServerConfigTest.java +++ b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/JaasServerConfigTest.java @@ -85,7 +85,7 @@ private void testJwksValidatorOptions() throws IOException { "keysEndpointUri", "https://sso/jwks", "usernameClaim", "username-claim", "fallbackUsernameClaim", "fallback-username-claim", - "fallbackUsernamePrefix", "username-prefix", + "fallbackUsernamePrefix", "fallback-username-prefix", "groupsClaimQuery", "\\$\\.groups", "groupsClaimDelimiter", ",", "validIssuerUri", "https://sso", @@ -239,7 +239,7 @@ private void testIntrospectValidatorOptions() throws IOException { "audience", "client-id", "usernameClaim", "username-claim", "fallbackUsernameClaim", "fallback-username-claim", - "fallbackUsernamePrefix", "username-prefix", + "fallbackUsernamePrefix", "fallback-username-prefix", "customClaimCheck", "@\\.aud anyof \\['kafka', 'something'\\]", "connectTimeoutSeconds", "10", "readTimeoutSeconds", "10", diff --git a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/PasswordAuthTest.java b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/PasswordAuthAndPrincipalExtractionTest.java similarity index 84% rename from testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/PasswordAuthTest.java rename to testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/PasswordAuthAndPrincipalExtractionTest.java index 964df62a..57981fc7 100644 --- a/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/PasswordAuthTest.java +++ b/testsuite/mockoauth-tests/src/test/java/io/strimzi/testsuite/oauth/mockoauth/PasswordAuthAndPrincipalExtractionTest.java @@ -4,10 +4,14 @@ */ package io.strimzi.testsuite.oauth.mockoauth; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.nimbusds.jose.JWSObject; import io.strimzi.kafka.oauth.common.HttpException; import io.strimzi.kafka.oauth.common.HttpUtil; +import io.strimzi.kafka.oauth.common.JSONUtil; import io.strimzi.kafka.oauth.common.OAuthAuthenticator; +import io.strimzi.kafka.oauth.common.PrincipalExtractor; import io.strimzi.kafka.oauth.common.SSLUtil; import io.strimzi.kafka.oauth.common.TokenInfo; import io.strimzi.kafka.oauth.common.TokenIntrospection; @@ -17,6 +21,7 @@ import javax.net.ssl.SSLSocketFactory; import java.net.URI; +import java.text.ParseException; import static io.strimzi.testsuite.oauth.mockoauth.Common.WWW_FORM_CONTENT_TYPE; import static io.strimzi.testsuite.oauth.mockoauth.Common.changeAuthServerMode; @@ -24,9 +29,9 @@ import static io.strimzi.testsuite.oauth.mockoauth.Common.createOAuthUser; import static io.strimzi.testsuite.oauth.mockoauth.Common.revokeToken; -public class PasswordAuthTest { +public class PasswordAuthAndPrincipalExtractionTest { - private static final Logger log = LoggerFactory.getLogger(PasswordAuthTest.class); + private static final Logger log = LoggerFactory.getLogger(PasswordAuthAndPrincipalExtractionTest.class); public void doTest() throws Exception { @@ -52,6 +57,7 @@ public void doTest() throws Exception { SSLSocketFactory sslFactory = SSLUtil.createSSLFactory( projectRoot + "/../docker/certificates/ca-truststore.p12", null, "changeit", null, null); + PrincipalExtractor principalExtractor = new PrincipalExtractor("username", "pref_", "clientId", "pref_service-account-"); // authenticate user against token endpoint with the correct password TokenInfo tokenInfo = OAuthAuthenticator.loginWithPassword( @@ -73,6 +79,10 @@ public void doTest() throws Exception { TokenIntrospection.debugLogJWT(log, token); + String principal = principalExtractor.getPrincipal(JSONUtil.readJSON(getAccessTokenPayload(token), JsonNode.class)); + Assert.assertEquals("Principal should be: 'pref_" + user1 + "'", "pref_" + user1, principal); + + ObjectNode json; // introspect the token using the introspection endpoint @@ -129,6 +139,9 @@ public void doTest() throws Exception { TokenIntrospection.debugLogJWT(log, token); + principal = principalExtractor.getPrincipal(JSONUtil.readJSON(getAccessTokenPayload(token), JsonNode.class)); + Assert.assertEquals("Principal should be: 'pref_service-account-" + client1 + "'", "pref_service-account-" + client1, principal); + // introspect the token using the introspection endpoint json = HttpUtil.post(URI.create("https://mockoauth:8090/introspect"), sslFactory, null, "Basic " + OAuthAuthenticator.base64encode(clientSrv + ':' + clientSrvSecret), WWW_FORM_CONTENT_TYPE, "token=" + token, ObjectNode.class); @@ -138,4 +151,9 @@ public void doTest() throws Exception { Assert.assertEquals("Introspection endpoint response contains `client_id`", client1, json.get("client_id") != null ? json.get("client_id").asText() : null); Assert.assertNull("Introspection endpoint response does not contain `username`", json.get("username")); } + + private String getAccessTokenPayload(String token) throws ParseException { + JWSObject jws = JWSObject.parse(token); + return jws.getPayload().toString(); + } }