Repository: cxf Updated Branches: refs/heads/3.1.x-fixes d9ddf0224 -> b13d37d92
[CXF-6692] Adding a local JWT validation handler Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/b13d37d9 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/b13d37d9 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/b13d37d9 Branch: refs/heads/3.1.x-fixes Commit: b13d37d92e3105298b55e3fbfee0c9d077e0817c Parents: d9ddf02 Author: Sergey Beryozkin <[email protected]> Authored: Tue Aug 9 16:25:07 2016 +0100 Committer: Sergey Beryozkin <[email protected]> Committed: Tue Aug 9 16:26:59 2016 +0100 ---------------------------------------------------------------------- .../filters/LocalJwtAccessTokenValidator.java | 108 +++++++++++++++++++ .../provider/AbstractOAuthDataProvider.java | 2 +- .../oauth2/filters/OAuth2JwtFiltersTest.java | 27 ++++- .../oauth2/filters/filters-serverJwt.xml | 28 ++++- 4 files changed, 157 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/b13d37d9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java new file mode 100644 index 0000000..3a83b36 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/LocalJwtAccessTokenValidator.java @@ -0,0 +1,108 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.oauth2.filters; + +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import javax.ws.rs.core.MultivaluedMap; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.common.AccessTokenValidation; +import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.provider.AccessTokenValidator; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; + +public class LocalJwtAccessTokenValidator extends JoseJwtConsumer implements AccessTokenValidator { + + public List<String> getSupportedAuthorizationSchemes() { + return Collections.singletonList(OAuthConstants.BEARER_AUTHORIZATION_SCHEME); + } + + public AccessTokenValidation validateAccessToken(MessageContext mc, + String authScheme, + String authSchemeData, + MultivaluedMap<String, String> extraProps) + throws OAuthServiceException { + try { + JwtToken token = super.getJwtToken(authSchemeData); + return convertClaimsToValidation(token.getClaims()); + } catch (Exception ex) { + throw new OAuthServiceException(ex); + } + } + + + private AccessTokenValidation convertClaimsToValidation(JwtClaims claims) { + AccessTokenValidation atv = new AccessTokenValidation(); + atv.setInitialValidationSuccessful(true); + if (claims.getAudience() != null) { + atv.setClientId(claims.getAudience()); + } + if (claims.getIssuedAt() != null) { + atv.setTokenIssuedAt(claims.getIssuedAt()); + } else { + atv.setTokenIssuedAt(new Date().getTime()); + } + if (claims.getExpiryTime() != null) { + atv.setTokenLifetime(claims.getExpiryTime() - atv.getTokenIssuedAt()); + } + Object resourceAud = claims.getClaim("resource"); + if (resourceAud != null) { + atv.setAudiences(resourceAud instanceof List ? CastUtils.cast((List<?>)resourceAud) + : Collections.<String>singletonList((String)resourceAud)); + } + if (claims.getIssuer() != null) { + atv.setTokenIssuer(claims.getIssuer()); + } + Object scope = claims.getClaim("scope"); + if (scope != null) { + String[] scopes = scope instanceof String + ? scope.toString().split(" ") : CastUtils.cast((List<?>)scope).toArray(new String[]{}); + List<OAuthPermission> perms = new LinkedList<OAuthPermission>(); + for (String s : scopes) { + if (!StringUtils.isEmpty(s)) { + perms.add(new OAuthPermission(s.trim())); + } + } + atv.setTokenScopes(perms); + } + String username = (String)claims.getClaim("preferred_username"); + if (username != null) { + UserSubject userSubject = new UserSubject(username); + if (claims.getSubject() != null) { + userSubject.setId(claims.getSubject()); + } + atv.setTokenSubject(userSubject); + } else if (claims.getSubject() != null) { + atv.setTokenSubject(new UserSubject(claims.getSubject())); + } + return atv; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/b13d37d9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java index 4a7b054..d2431ea 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java @@ -115,7 +115,7 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, Cl OAuthUtils.convertPermissionsToScopeList(at.getScopes())); } // OAuth2 resource indicators (resource server audience) - if (at.getAudiences().isEmpty()) { + if (!at.getAudiences().isEmpty()) { List<String> resourceAudiences = at.getAudiences(); claims.setClaim("resource", resourceAudiences.size() == 1 ? resourceAudiences.get(0) : resourceAudiences); http://git-wip-us.apache.org/repos/asf/cxf/blob/b13d37d9/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java index 98ba853..02acacc 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/filters/OAuth2JwtFiltersTest.java @@ -50,7 +50,16 @@ public class OAuth2JwtFiltersTest extends AbstractBusClientServerTestBase { launchServer(BookServerOAuth2ServiceJwt.class, true)); } @org.junit.Test - public void testServiceWithJwtTokenAndScope() throws Exception { + public void testServiceWithJwtToken() throws Exception { + String rsAddress = "https://localhost:" + PORT + "/secured/bookstore/books"; + doTestServiceWithJwtTokenAndScope(rsAddress); + } + @org.junit.Test + public void testServiceWithJwtTokenAndLocalValidation() throws Exception { + String rsAddress = "https://localhost:" + PORT + "/securedLocalValidation/bookstore/books"; + doTestServiceWithJwtTokenAndScope(rsAddress); + } + private void doTestServiceWithJwtTokenAndScope(String rsAddress) throws Exception { URL busFile = OAuth2JwtFiltersTest.class.getResource("client.xml"); // Get Authorization Code @@ -84,8 +93,7 @@ public class OAuth2JwtFiltersTest extends AbstractBusClientServerTestBase { assertEquals("consumer-id", claims.getAudience()); assertEquals("alice", claims.getSubject()); // Now invoke on the service with the access token - String address = "https://localhost:" + PORT + "/secured/bookstore/books"; - WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + WebClient client = WebClient.create(rsAddress, OAuth2TestUtils.setupProviders(), busFile.toString()); client.header("Authorization", "Bearer " + accessToken.getTokenKey()); @@ -96,4 +104,17 @@ public class OAuth2JwtFiltersTest extends AbstractBusClientServerTestBase { assertEquals(returnedBook.getName(), "book"); assertEquals(returnedBook.getId(), 123L); } + + @org.junit.Test + public void testServiceLocalValidationWithNoToken() throws Exception { + URL busFile = OAuth2FiltersTest.class.getResource("client.xml"); + + // Now invoke on the service with the faked access token + String address = "https://localhost:" + PORT + "/securedLocalValidation/bookstore/books"; + WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + busFile.toString()); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/b13d37d9/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml ---------------------------------------------------------------------- diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml index 0d3077a..9ef8099 100644 --- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/oauth2/filters/filters-serverJwt.xml @@ -85,12 +85,12 @@ under the License. <bean id="tvServiceClient" factory-bean="tvServiceClientFactory" factory-method="createWebClient"/> - <bean id="tokenValidator" class="org.apache.cxf.rs.security.oauth2.filters.AccessTokenValidatorClient"> + <bean id="remoteTokenValidator" class="org.apache.cxf.rs.security.oauth2.filters.AccessTokenValidatorClient"> <property name="tokenValidatorClient" ref="tvServiceClient"/> </bean> - <bean id="oAuthFilter" class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter"> - <property name="tokenValidator" ref="tokenValidator"/> + <bean id="oAuthFilterRemoteValidation" class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter"> + <property name="tokenValidator" ref="remoteTokenValidator"/> </bean> <jaxrs:server @@ -100,10 +100,30 @@ under the License. <ref bean="serviceBean"/> </jaxrs:serviceBeans> <jaxrs:providers> - <ref bean="oAuthFilter"/> + <ref bean="oAuthFilterRemoteValidation"/> </jaxrs:providers> </jaxrs:server> + <bean id="localTokenValidator" class="org.apache.cxf.rs.security.oauth2.filters.LocalJwtAccessTokenValidator"/> + + <bean id="oAuthFilterLocalValidation" class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter"> + <property name="tokenValidator" ref="localTokenValidator"/> + </bean> + + <jaxrs:server + depends-on="tls-config" + address="https://localhost:${testutil.ports.jaxrs-oauth2-filtersJwt}/securedLocalValidation"> + <jaxrs:serviceBeans> + <ref bean="serviceBean"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <ref bean="oAuthFilterLocalValidation"/> + </jaxrs:providers> + <jaxrs:properties> + <entry key="rs.security.signature.in.properties" value="org/apache/cxf/systest/jaxrs/security/alice.rs.properties"/> + </jaxrs:properties> + </jaxrs:server> + <http:conduit name="https://localhost.*"> <http:client ConnectionTimeout="3000000" ReceiveTimeout="3000000"/> <http:tlsClientParameters disableCNCheck="true">
