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

Reply via email to