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 <[email protected]>
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());
}