This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/openwebbeans-meecrowave.git
The following commit(s) were added to refs/heads/master by this push: new 8e3a59c [MEECROWAVE-267] oauth2 claim support (basic) 8e3a59c is described below commit 8e3a59c47f24fdd6b537faf55756fd627a443414 Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Sun Nov 8 21:06:40 2020 +0100 [MEECROWAVE-267] oauth2 claim support (basic) --- meecrowave-core/pom.xml | 13 ++ meecrowave-junit/pom.xml | 13 ++ meecrowave-oauth2/pom.xml | 47 ++++ .../oauth2/configuration/OAuth2Configurer.java | 251 +++++++++++++++++---- .../oauth2/configuration/OAuth2Options.java | 11 + .../OAuth2AuthorizationCodeGrantService.java | 40 +++- .../org/apache/meecrowave/oauth2/OAuth2Test.java | 75 +++--- 7 files changed, 361 insertions(+), 89 deletions(-) diff --git a/meecrowave-core/pom.xml b/meecrowave-core/pom.xml index d2e71a3..48726f1 100644 --- a/meecrowave-core/pom.xml +++ b/meecrowave-core/pom.xml @@ -32,6 +32,19 @@ <meecrowave.build.name>${project.groupId}.core</meecrowave.build.name> </properties> + <profiles> + <profile> + <id>dev</id> <!-- idea does not see that it is a shade so workaround it with an IDE profile --> + <dependencies> + <dependency> + <groupId>org.apache.meecrowave</groupId> + <artifactId>meecrowave-specs-api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </profile> + </profiles> + <dependencies> <dependency> <groupId>org.apache.meecrowave</groupId> diff --git a/meecrowave-junit/pom.xml b/meecrowave-junit/pom.xml index 9cebb27..08dfcbf 100644 --- a/meecrowave-junit/pom.xml +++ b/meecrowave-junit/pom.xml @@ -28,6 +28,19 @@ <artifactId>meecrowave-junit</artifactId> <name>Meecrowave :: JUnit</name> + <profiles> + <profile> + <id>dev</id> <!-- idea does not see that it is a shade so workaround it with an IDE profile --> + <dependencies> + <dependency> + <groupId>org.apache.meecrowave</groupId> + <artifactId>meecrowave-specs-api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </profile> + </profiles> + <dependencies> <dependency> <groupId>junit</groupId> diff --git a/meecrowave-oauth2/pom.xml b/meecrowave-oauth2/pom.xml index a7c13fc..193ea6e 100644 --- a/meecrowave-oauth2/pom.xml +++ b/meecrowave-oauth2/pom.xml @@ -32,6 +32,19 @@ <meecrowave.build.name>${project.groupId}.oauth2</meecrowave.build.name> </properties> + <profiles> + <profile> + <id>dev</id> <!-- idea does not see that it is a shade so workaround it with an IDE profile --> + <dependencies> + <dependency> + <groupId>org.apache.meecrowave</groupId> + <artifactId>meecrowave-specs-api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </profile> + </profiles> + <dependencies> <dependency> <groupId>org.apache.meecrowave</groupId> @@ -42,6 +55,40 @@ <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-rs-security-oauth2</artifactId> <version>${cxf.version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jta_1.1_spec</artifactId> + </exclusion> + <exclusion> + <groupId>jakarta.xml.ws</groupId> + <artifactId>jakarta.xml.ws-api</artifactId> + </exclusion> + <exclusion> + <groupId>jakarta.xml.soap</groupId> + <artifactId>jakarta.xml.soap-api</artifactId> + </exclusion> + <exclusion> + <groupId>jakarta.annotation</groupId> + <artifactId>jakarta.annotation-api</artifactId> + </exclusion> + <exclusion> + <groupId>jakarta.jws</groupId> + <artifactId>jakarta.jws-api</artifactId> + </exclusion> + <exclusion> + <groupId>com.sun.activation</groupId> + <artifactId>jakarta.activation</artifactId> + </exclusion> + <exclusion> + <groupId>org.jboss.spec.javax.rmi</groupId> + <artifactId>jboss-rmi-api_1.0_spec</artifactId> + </exclusion> + <exclusion> + <groupId>com.sun.xml.messaging.saaj</groupId> + <artifactId>saaj-impl</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> diff --git a/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Configurer.java b/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Configurer.java index cb6c2f0..94f9865 100644 --- a/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Configurer.java +++ b/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Configurer.java @@ -18,41 +18,12 @@ */ package org.apache.meecrowave.oauth2.configuration; -import static java.util.Arrays.asList; -import static java.util.Collections.emptySet; -import static java.util.Locale.ENGLISH; -import static java.util.Optional.ofNullable; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toMap; -import static org.apache.cxf.rs.security.oauth2.common.AuthenticationMethod.PASSWORD; - -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.security.Principal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.function.Consumer; -import java.util.function.Function; - -import javax.annotation.PostConstruct; -import javax.crypto.spec.SecretKeySpec; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; - import org.apache.catalina.realm.GenericPrincipal; import org.apache.cxf.Bus; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.interceptor.security.AuthenticationException; import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.message.Message; import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; @@ -61,10 +32,10 @@ import org.apache.cxf.rs.security.jose.jwe.JweUtils; import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; import org.apache.cxf.rs.security.jose.jws.JwsUtils; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; -import org.apache.cxf.rs.security.oauth2.common.AccessToken; +import org.apache.cxf.rs.security.oauth2.common.AuthenticationMethod; +import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rs.security.oauth2.common.OAuthRedirectionState; import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; -import org.apache.cxf.rs.security.oauth2.common.TokenIntrospection; import org.apache.cxf.rs.security.oauth2.common.UserSubject; import org.apache.cxf.rs.security.oauth2.grants.AbstractGrantHandler; import org.apache.cxf.rs.security.oauth2.grants.clientcred.ClientCredentialsGrantHandler; @@ -93,6 +64,34 @@ import org.apache.meecrowave.Meecrowave; import org.apache.meecrowave.oauth2.data.RefreshTokenEnabledProvider; import org.apache.meecrowave.oauth2.provider.JCacheCodeDataProvider; +import javax.annotation.PostConstruct; +import javax.crypto.spec.SecretKeySpec; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.MultivaluedMap; +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.function.Consumer; +import java.util.function.Function; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static java.util.Locale.ENGLISH; +import static java.util.Optional.ofNullable; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; +import static org.apache.cxf.rs.security.oauth2.common.AuthenticationMethod.PASSWORD; + @ApplicationScoped public class OAuth2Configurer { @Inject @@ -134,6 +133,13 @@ public class OAuth2Configurer { protected JwtClaims createJwtAccessToken(final ServerAccessToken at) { return customizeClaims.apply(super.createJwtAccessToken(at)); } + + @Override + protected ServerAccessToken createNewAccessToken(final Client client, final UserSubject userSub) { + final ServerAccessToken token = super.createNewAccessToken(client, userSub); + forwardClaims(client, userSub, token); + return token; + } }; jpaProvider.setEntityManagerFactory(JPAAdapter.createEntityManagerFactory(configuration)); provider = jpaProvider; @@ -146,6 +152,13 @@ public class OAuth2Configurer { protected JwtClaims createJwtAccessToken(final ServerAccessToken at) { return customizeClaims.apply(super.createJwtAccessToken(at)); } + + @Override + protected ServerAccessToken createNewAccessToken(final Client client, final UserSubject userSub) { + final ServerAccessToken token = super.createNewAccessToken(client, userSub); + forwardClaims(client, userSub, token); + return token; + } }; jpaProvider.setEntityManagerFactory(JPAAdapter.createEntityManagerFactory(configuration)); provider = jpaProvider; @@ -160,6 +173,13 @@ public class OAuth2Configurer { protected JwtClaims createJwtAccessToken(final ServerAccessToken at) { return customizeClaims.apply(super.createJwtAccessToken(at)); } + + @Override + protected ServerAccessToken createNewAccessToken(final Client client, final UserSubject userSub) { + final ServerAccessToken token = super.createNewAccessToken(client, userSub); + forwardClaims(client, userSub, token); + return token; + } }; } catch (final Exception e) { throw new IllegalStateException(e); @@ -174,6 +194,13 @@ public class OAuth2Configurer { protected JwtClaims createJwtAccessToken(final ServerAccessToken at) { return customizeClaims.apply(super.createJwtAccessToken(at)); } + + @Override + protected ServerAccessToken createNewAccessToken(final Client client, final UserSubject userSub) { + final ServerAccessToken token = super.createNewAccessToken(client, userSub); + forwardClaims(client, userSub, token); + return token; + } }; } catch (final Exception e) { throw new IllegalStateException(e); @@ -187,6 +214,13 @@ public class OAuth2Configurer { protected JwtClaims createJwtAccessToken(final ServerAccessToken at) { return customizeClaims.apply(super.createJwtAccessToken(at)); } + + @Override + protected ServerAccessToken createNewAccessToken(final Client client, final UserSubject userSub) { + final ServerAccessToken token = super.createNewAccessToken(client, userSub); + forwardClaims(client, userSub, token); + return token; + } }; break; } @@ -197,27 +231,45 @@ public class OAuth2Configurer { protected JwtClaims createJwtAccessToken(final ServerAccessToken at) { return customizeClaims.apply(super.createJwtAccessToken(at)); } + + @Override + protected ServerAccessToken createNewAccessToken(final Client client, final UserSubject userSub) { + final ServerAccessToken token = super.createNewAccessToken(client, userSub); + forwardClaims(client, userSub, token); + return token; + } }; break; default: throw new IllegalArgumentException("Unsupported oauth2 provider: " + configuration.getProvider()); } - final RefreshTokenGrantHandler refreshTokenGrantHandler = new RefreshTokenGrantHandler(); + final RefreshTokenGrantHandler refreshTokenGrantHandler = new RefreshTokenGrantHandler() { + @Override + public ServerAccessToken createAccessToken(final Client client, + final MultivaluedMap<String, String> params) throws OAuthServiceException { + final ServerAccessToken accessToken = super.createAccessToken(client, params); + forwardClaims(client, accessToken.getSubject(), accessToken); + return accessToken; + } + }; refreshTokenGrantHandler.setDataProvider(provider); refreshTokenGrantHandler.setUseAllClientScopes(configuration.isUseAllClientScopes()); refreshTokenGrantHandler.setPartialMatchScopeValidation(configuration.isPartialMatchScopeValidation()); - final ResourceOwnerLoginHandler loginHandler = configuration.isJaas() ? new JAASResourceOwnerLoginHandler() : (client, name, password) -> { + final ResourceOwnerLoginHandler loginHandler = configuration.isJaas() ? new JAASResourceOwnerLoginHandler() { + @Override + public UserSubject createSubject(final Client client, final String name, final String password) { + final UserSubject subject = super.createSubject(client, name, password); + forwardRolesAsClaims(subject); + return subject; + } + } : (client, name, password) -> { try { request.login(name, password); try { final Principal pcp = request.getUserPrincipal(); - final List<String> roles = GenericPrincipal.class.isInstance(pcp) ? - new ArrayList<>(asList(GenericPrincipal.class.cast(pcp).getRoles())) : Collections.<String>emptyList(); - final UserSubject userSubject = new UserSubject(name, roles); - userSubject.setAuthenticationMethod(PASSWORD); - return userSubject; + return doCreateUserSubject(pcp); } finally { request.logout(); } @@ -228,12 +280,58 @@ public class OAuth2Configurer { final List<AccessTokenGrantHandler> handlers = new ArrayList<>(); handlers.add(refreshTokenGrantHandler); - handlers.add(new ClientCredentialsGrantHandler()); - handlers.add(new ResourceOwnerGrantHandler() {{ - setLoginHandler(loginHandler); - }}); - handlers.add(new AuthorizationCodeGrantHandler()); - handlers.add(new JwtBearerGrantHandler()); + handlers.add(new ClientCredentialsGrantHandler() { + @Override + protected ServerAccessToken doCreateAccessToken(final Client client, + final UserSubject subject, + final String requestedGrant, + final List<String> requestedScopes, + final List<String> audiences) { + final ServerAccessToken serverAccessToken = super.doCreateAccessToken(client, subject, requestedGrant, requestedScopes, audiences); + forwardClaims(client, subject, serverAccessToken); + return serverAccessToken; + } + }); + handlers.add(new ResourceOwnerGrantHandler() { + { + setLoginHandler(loginHandler); + } + + @Override + protected ServerAccessToken doCreateAccessToken(final Client client, + final UserSubject subject, + final String requestedGrant, + final List<String> requestedScopes, + final List<String> audiences) { + final ServerAccessToken serverAccessToken = super.doCreateAccessToken(client, subject, requestedGrant, requestedScopes, audiences); + forwardClaims(client, subject, serverAccessToken); + return serverAccessToken; + } + }); + handlers.add(new AuthorizationCodeGrantHandler() { + @Override + protected ServerAccessToken doCreateAccessToken(final Client client, + final UserSubject subject, + final String requestedGrant, + final List<String> requestedScopes, + final List<String> audiences) { + final ServerAccessToken serverAccessToken = super.doCreateAccessToken(client, subject, requestedGrant, requestedScopes, audiences); + forwardClaims(client, subject, serverAccessToken); + return serverAccessToken; + } + }); + handlers.add(new JwtBearerGrantHandler() { + @Override + protected ServerAccessToken doCreateAccessToken(final Client client, + final UserSubject subject, + final String requestedGrant, + final List<String> requestedScopes, + final List<String> audiences) { + final ServerAccessToken serverAccessToken = super.doCreateAccessToken(client, subject, requestedGrant, requestedScopes, audiences); + forwardClaims(client, subject, serverAccessToken); + return serverAccessToken; + } + }); provider.setUseJwtFormatForAccessTokens(configuration.isUseJwtFormatForAccessTokens()); provider.setAccessTokenLifetime(configuration.getAccessTokenLifetime()); @@ -350,6 +448,69 @@ public class OAuth2Configurer { }; } + public UserSubject doCreateUserSubject(final Principal pcp) { + final List<String> roles = GenericPrincipal.class.isInstance(pcp) ? + new ArrayList<>(asList(GenericPrincipal.class.cast(pcp).getRoles())) : Collections.<String>emptyList(); + final String name = pcp.getName(); + final UserSubject userSubject = new UserSubject(name, name, roles); + final Message m = JAXRSUtils.getCurrentMessage(); + if (m != null && m.get(AuthenticationMethod.class) != null) { + userSubject.setAuthenticationMethod(m.get(AuthenticationMethod.class)); + } else { + userSubject.setAuthenticationMethod(PASSWORD); + } + forwardRolesAsClaims(userSubject); + return userSubject; + } + + private void forwardRolesAsClaims(final UserSubject subject) { + if (configuration.isForwardRoleAsJwtClaims() && subject.getRoles() != null) { + subject.setProperties(new HashMap<>()); + subject.getProperties().put("claim.roles", String.join(", ", subject.getRoles())); + } + } + + private void forwardClaims(final Client client, final UserSubject subject, + final ServerAccessToken serverAccessToken) { + forwardClientClaims(client, serverAccessToken); + forwardUserClaims(subject, serverAccessToken); + } + + private void forwardUserClaims(final UserSubject subject, + final ServerAccessToken serverAccessToken) { + if (subject.getProperties() == null || subject.getProperties().isEmpty()) { + return; + } + final Map<String, String> claims = subject.getProperties().entrySet().stream() + .filter(it -> it.getKey().startsWith("claim.")) + .collect(toMap(it -> it.getKey().substring("claim.".length()), Map.Entry::getValue)); + if (claims.isEmpty()) { + return; + } + if (serverAccessToken.getExtraProperties() == null) { + serverAccessToken.setExtraProperties(claims); + } else { + serverAccessToken.getExtraProperties().putAll(claims); + } + } + + private void forwardClientClaims(final Client client, final ServerAccessToken serverAccessToken) { + if (client.getProperties() == null || client.getProperties().isEmpty()) { + return; + } + final Map<String, String> claims = client.getProperties().entrySet().stream() + .filter(it -> it.getKey().startsWith("claim.")) + .collect(toMap(it -> it.getKey().substring("claim.".length()), Map.Entry::getValue)); + if (claims.isEmpty()) { + return; + } + if (serverAccessToken.getExtraProperties() == null) { + serverAccessToken.setExtraProperties(claims); + } else { + serverAccessToken.getExtraProperties().putAll(claims); + } + } + private void forwardSecurityProperties() { // TODO: make it even more contextual, client based? final Message currentMessage = PhaseInterceptorChain.getCurrentMessage(); diff --git a/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Options.java b/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Options.java index 5b469d6..1190c8e 100644 --- a/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Options.java +++ b/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/configuration/OAuth2Options.java @@ -49,6 +49,9 @@ public class OAuth2Options implements Cli.Options { @CliOption(name = "oauth2-use-jaas", description = "Should jaas be used - alternative (default) is to delegate to meecrowave/tomcat realms") private boolean jaas; + @CliOption(name = "oauth2-forward-role-as-jwt-claims", description = "Should jaas be used - alternative (default) is to delegate to meecrowave/tomcat realms") + private boolean forwardRoleAsJwtClaims; + @CliOption(name = "oauth2-access-token-lifetime", description = "How long an access token is valid, default to 3600s") private int accessTokenLifetime = 3600; @@ -172,6 +175,14 @@ public class OAuth2Options implements Cli.Options { @CliOption(name = "oauth2-redirection-scopes-requiring-no-consent", description = "For authorization code flow, the scopes using no consent") private String scopesRequiringNoConsent; + public boolean isForwardRoleAsJwtClaims() { + return forwardRoleAsJwtClaims; + } + + public void setForwardRoleAsJwtClaims(final boolean forwardRoleAsJwtClaims) { + this.forwardRoleAsJwtClaims = forwardRoleAsJwtClaims; + } + public String getEncryptedAlgo() { return encryptedAlgo; } diff --git a/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/resource/OAuth2AuthorizationCodeGrantService.java b/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/resource/OAuth2AuthorizationCodeGrantService.java index 1eee853..536f322 100644 --- a/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/resource/OAuth2AuthorizationCodeGrantService.java +++ b/meecrowave-oauth2/src/main/java/org/apache/meecrowave/oauth2/resource/OAuth2AuthorizationCodeGrantService.java @@ -18,11 +18,12 @@ */ package org.apache.meecrowave.oauth2.resource; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.APPLICATION_XHTML_XML; -import static javax.ws.rs.core.MediaType.APPLICATION_XML; -import static javax.ws.rs.core.MediaType.TEXT_HTML; -import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.services.AuthorizationCodeGrantService; +import org.apache.cxf.rs.security.oauth2.services.RedirectionBasedGrantService; +import org.apache.cxf.security.SecurityContext; +import org.apache.meecrowave.oauth2.configuration.OAuth2Configurer; import javax.enterprise.context.RequestScoped; import javax.enterprise.inject.Typed; @@ -34,10 +35,13 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import java.security.Principal; -import org.apache.cxf.rs.security.oauth2.services.AuthorizationCodeGrantService; -import org.apache.cxf.rs.security.oauth2.services.RedirectionBasedGrantService; -import org.apache.meecrowave.oauth2.configuration.OAuth2Configurer; +import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.APPLICATION_XHTML_XML; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static javax.ws.rs.core.MediaType.TEXT_HTML; @RequestScoped @Path("authorize") @@ -51,7 +55,7 @@ public class OAuth2AuthorizationCodeGrantService extends AuthorizationCodeGrantS @Override @GET - @Produces({ APPLICATION_XHTML_XML, TEXT_HTML, APPLICATION_XML, APPLICATION_JSON }) + @Produces({APPLICATION_XHTML_XML, TEXT_HTML, APPLICATION_XML, APPLICATION_JSON}) public Response authorize() { return getDelegate().authorize(); } @@ -73,6 +77,7 @@ public class OAuth2AuthorizationCodeGrantService extends AuthorizationCodeGrantS private RedirectionBasedGrantService getDelegate() { delegate.setMessageContext(getMessageContext()); + delegate.setConfigurer(configurer); configurer.accept(delegate); return delegate; } @@ -80,5 +85,22 @@ public class OAuth2AuthorizationCodeGrantService extends AuthorizationCodeGrantS @RequestScoped @Typed(LazyImpl.class) static class LazyImpl extends AuthorizationCodeGrantService { + private OAuth2Configurer configurer; + + public void setConfigurer(final OAuth2Configurer configurer) { + this.configurer = configurer; + } + + @Override + protected UserSubject createUserSubject(final SecurityContext securityContext, + final MultivaluedMap<String, String> params) { + final MessageContext mc = getMessageContext(); + final UserSubject subject = mc.getContent(UserSubject.class); + if (subject != null) { + return subject; + } + final Principal principal = securityContext.getUserPrincipal(); + return configurer.doCreateUserSubject(principal); + } } } diff --git a/meecrowave-oauth2/src/test/java/org/apache/meecrowave/oauth2/OAuth2Test.java b/meecrowave-oauth2/src/test/java/org/apache/meecrowave/oauth2/OAuth2Test.java index 0c69be4..ad7460a 100644 --- a/meecrowave-oauth2/src/test/java/org/apache/meecrowave/oauth2/OAuth2Test.java +++ b/meecrowave-oauth2/src/test/java/org/apache/meecrowave/oauth2/OAuth2Test.java @@ -18,41 +18,6 @@ */ package org.apache.meecrowave.oauth2; -import static java.util.Collections.singletonList; -import static javax.ws.rs.client.Entity.entity; -import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.security.PublicKey; -import java.util.Base64; -import java.util.function.BiFunction; -import java.util.stream.Stream; - -import javax.cache.Cache; -import javax.cache.CacheManager; -import javax.cache.Caching; -import javax.cache.configuration.MutableConfiguration; -import javax.cache.spi.CachingProvider; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.bind.Jsonb; -import javax.json.bind.JsonbBuilder; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Form; -import javax.ws.rs.core.Response; - import org.apache.cxf.common.classloader.ClassLoaderUtils; import org.apache.cxf.message.Message; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; @@ -72,6 +37,40 @@ import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.configuration.MutableConfiguration; +import javax.cache.spi.CachingProvider; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.Response; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.PublicKey; +import java.util.Base64; +import java.util.function.BiFunction; +import java.util.stream.Stream; + +import static java.util.Collections.singletonList; +import static javax.ws.rs.client.Entity.entity; +import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + public class OAuth2Test { private static final File KEYSTORE = new File("target/OAuth2Test/keystore.jceks"); private static PublicKey PUBLIC_KEY; @@ -85,6 +84,8 @@ public class OAuth2Test { .property("oauth2-jwt-issuer", "myissuer") // auth code support is optional so activate it .property("oauth2-authorization-code-support", "true") + // to ensure this toggle does not trigger any exception + .property("oauth2-forward-role-as-jwt-claims", "true") // auth code jose setup to store the tokens .property("oauth2.cxf.rs.security.keystore.type", "jks") .property("oauth2.cxf.rs.security.keystore.file", KEYSTORE.getAbsolutePath()) @@ -253,6 +254,10 @@ public class OAuth2Test { assertEquals("RS256", header.getString("alg")); assertEquals("test", payload.getString("username")); assertEquals(client, payload.getString("client_id")); + + final JsonObject extraProperties = payload.getJsonObject("extra_properties"); + assertNotNull(extraProperties); + assertEquals("admin", extraProperties.getString("roles")); } catch (final Exception e) { fail(e.getMessage()); }