From c4c03e06de3ec8eb8b3e3b35a8c27d1615fd5931 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Thu, 5 Nov 2015 10:33:21 -0500 Subject: [PATCH 0001/1346] Setup mergeinfo for 3.1.x-fixes --- .gitmergeinfo | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitmergeinfo diff --git a/.gitmergeinfo b/.gitmergeinfo new file mode 100644 index 00000000000..5a7428942aa --- /dev/null +++ b/.gitmergeinfo @@ -0,0 +1 @@ +origin/master From a3e1065d4c2a600f63585a6c892e636f5740cf73 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Thu, 5 Nov 2015 11:16:41 -0500 Subject: [PATCH 0002/1346] Update to use a version of bcel that will work with java8 --- systests/jibx/databinding-jibx/pom.xml | 7 +++++++ systests/jibx/jaxrs-jibx/pom.xml | 7 +++++++ systests/pom.xml | 23 +---------------------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/systests/jibx/databinding-jibx/pom.xml b/systests/jibx/databinding-jibx/pom.xml index 0b3742eb581..fa592760d85 100644 --- a/systests/jibx/databinding-jibx/pom.xml +++ b/systests/jibx/databinding-jibx/pom.xml @@ -104,6 +104,13 @@ org.jibx maven-jibx-plugin ${cxf.jibx.version} + + + com.google.code.findbugs + bcel-findbugs + 6.0 + + target/generated/src/test/java/jibx_bindings diff --git a/systests/jibx/jaxrs-jibx/pom.xml b/systests/jibx/jaxrs-jibx/pom.xml index fd02011fb50..6a8e5855926 100644 --- a/systests/jibx/jaxrs-jibx/pom.xml +++ b/systests/jibx/jaxrs-jibx/pom.xml @@ -251,6 +251,13 @@ org.jibx jibx-maven-plugin ${cxf.jibx.version} + + + com.google.code.findbugs + bcel-findbugs + 6.0 + + generate-java-code-from-schema diff --git a/systests/pom.xml b/systests/pom.xml index 39c2a17bbc5..21e0e38464b 100644 --- a/systests/pom.xml +++ b/systests/pom.xml @@ -49,27 +49,6 @@ cdi rs-http-sci tracing + jibx - - - - - jdk-7 - - jibx - - - 1.7 - - - - jdk-6 - - jibx - - - 1.6 - - - From f68a90ad7d8339395db1ab7a8ac551544bb9570e Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Thu, 5 Nov 2015 11:18:05 -0500 Subject: [PATCH 0003/1346] Recording .gitmergeinfo Changes --- .gitmergeinfo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitmergeinfo b/.gitmergeinfo index 5a7428942aa..b4ea0145480 100644 --- a/.gitmergeinfo +++ b/.gitmergeinfo @@ -1 +1,3 @@ origin/master +B 65e1e07fdb810ec9de135530ca3e3d23821836a3 +B 7fc957efa3a193a5f2ae178b8a608717ce4c5b26 From b0010ae554719e961891adb06a3dcd0bee1eb6ba Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 5 Nov 2015 15:56:09 +0000 Subject: [PATCH 0004/1346] Support none signature for JWT tokens --- .../cxf/rs/security/jose/jws/JwsUtils.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index 3a9a91ec7fc..fcebd8495ed 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -301,16 +301,20 @@ private static JwsSignatureProvider loadSignatureProvider(Message m, } } else { String signatureAlgo = getSignatureAlgo(m, props, null, null); - PrivateKey pk = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.SIGN); - theSigProvider = getPrivateKeySignatureProvider(pk, - SignatureAlgorithm.getAlgorithm(signatureAlgo)); - if (includeCert) { - headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); - } - if (includeCertSha1) { - String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); - if (digest != null) { - headers.setX509Thumbprint(digest); + if (SignatureAlgorithm.getAlgorithm(signatureAlgo) == SignatureAlgorithm.NONE) { + theSigProvider = new NoneJwsSignatureProvider(); + } else { + PrivateKey pk = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.SIGN); + theSigProvider = getPrivateKeySignatureProvider(pk, + SignatureAlgorithm.getAlgorithm(signatureAlgo)); + if (includeCert) { + headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); + } + if (includeCertSha1) { + String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); + if (digest != null) { + headers.setX509Thumbprint(digest); + } } } } From c1716eef7073e2697c02f7e31a894403a93d1d9d Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 5 Nov 2015 16:27:18 +0000 Subject: [PATCH 0005/1346] Adding some JWT tests --- .../cxf/rs/security/jose/jwk/JwkUtils.java | 2 +- .../jose/jwejws/JweJwsAlgorithmTest.java | 5 + .../jose/jwt/BookServerJwtAlgorithms.java | 59 ++ .../security/jose/jwt/JWTAlgorithmTest.java | 609 ++++++++++++++++++ .../jwt/PrivateKeyPasswordProviderImpl.java | 40 ++ .../security/jose/jwt/algorithms-server.xml | 157 +++++ .../jaxrs/security/jose/jwt/client.xml | 38 ++ 7 files changed, 909 insertions(+), 1 deletion(-) create mode 100644 systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAlgorithms.java create mode 100644 systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAlgorithmTest.java create mode 100644 systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/PrivateKeyPasswordProviderImpl.java create mode 100644 systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/algorithms-server.xml create mode 100644 systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/client.xml diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index a85d4dc0db7..cd609f54fd9 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -512,7 +512,7 @@ private static JweHeaders toJweHeaders(String ct) { public static void includeCertChain(JsonWebKey jwk, JoseHeaders headers, String algo) { if (KeyType.RSA.equals(jwk.getKeyType())) { - List chain = CastUtils.cast((List)jwk.getProperty("x5c")); + List chain = CastUtils.cast((List)jwk.getProperty(JsonWebKey.X509_CHAIN)); if (chain != null) { headers.setX509Chain(chain); } diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java index ed94b4812c5..40b90101963 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java @@ -470,6 +470,11 @@ public void testManualSignature() throws Exception { String sig2 = sig.replace('y', 'z'); response = client.post(header + "." + payload + "." + sig2); assertNotEquals(response.getStatus(), 200); + + // Modified payload + String payload2 = payload.replace('y', 'z'); + response = client.post(header + "." + payload2 + "." + sig); + assertNotEquals(response.getStatus(), 200); } // 1024 bits not allowed with RSA according to the spec diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAlgorithms.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAlgorithms.java new file mode 100644 index 00000000000..681bc6eeab8 --- /dev/null +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAlgorithms.java @@ -0,0 +1,59 @@ +/** + * 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.systest.jaxrs.security.jose.jwt; + +import java.net.URL; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusTestServerBase; +import org.apache.cxf.testutil.common.TestUtil; + +public class BookServerJwtAlgorithms extends AbstractBusTestServerBase { + public static final String PORT = TestUtil.getPortNumber("jaxrs-jwt-algorithms"); + private static final URL SERVER_CONFIG_FILE = + BookServerJwtAlgorithms.class.getResource("algorithms-server.xml"); + + protected void run() { + SpringBusFactory bf = new SpringBusFactory(); + Bus springBus = bf.createBus(SERVER_CONFIG_FILE); + BusFactory.setDefaultBus(springBus); + setBus(springBus); + + try { + new BookServerJwtAlgorithms(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) { + try { + BookServerJwtAlgorithms s = new BookServerJwtAlgorithms(); + s.start(); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(-1); + } finally { + System.out.println("done!"); + } + } +} diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAlgorithmTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAlgorithmTest.java new file mode 100644 index 00000000000..4b66f359e48 --- /dev/null +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAlgorithmTest.java @@ -0,0 +1,609 @@ +/** + * 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.systest.jaxrs.security.jose.jwt; + +import java.net.URL; +import java.security.Security; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; + +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.rs.security.jose.jaxrs.JwtAuthenticationClientFilter; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.systest.jaxrs.security.Book; +import org.apache.cxf.systest.jaxrs.security.SecurityTestUtil; +import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * Some tests for JWT tokens. + */ +public class JWTAlgorithmTest extends AbstractBusClientServerTestBase { + public static final String PORT = BookServerJwtAlgorithms.PORT; + + @BeforeClass + public static void startServers() throws Exception { + assertTrue("server did not launch correctly", + launchServer(BookServerJwtAlgorithms.class, true)); + registerBouncyCastleIfNeeded(); + } + + private static void registerBouncyCastleIfNeeded() throws Exception { + // Still need it for Oracle Java 7 and Java 8 + Security.addProvider(new BouncyCastleProvider()); + } + + @AfterClass + public static void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + // + // Encryption tests + // + + @org.junit.Test + public void testEncryptionProperties() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + JwtAuthenticationClientFilter clientFilter = new JwtAuthenticationClientFilter(); + clientFilter.setJwsRequired(false); + clientFilter.setJweRequired(true); + providers.add(clientFilter); + + String address = "https://localhost:" + PORT + "/encryptedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.encryption.properties", + "org/apache/cxf/systest/jaxrs/security/bob.jwk.properties"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testEncryptionDynamic() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + JwtAuthenticationClientFilter clientFilter = new JwtAuthenticationClientFilter(); + clientFilter.setJwsRequired(false); + clientFilter.setJweRequired(true); + providers.add(clientFilter); + + String address = "https://localhost:" + PORT + "/encryptedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", "org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt"); + properties.put("rs.security.encryption.content.algorithm", "A128GCM"); + properties.put("rs.security.encryption.key.algorithm", "RSA-OAEP"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testWrongKeyEncryptionAlgorithm() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + JwtAuthenticationClientFilter clientFilter = new JwtAuthenticationClientFilter(); + clientFilter.setJwsRequired(false); + clientFilter.setJweRequired(true); + providers.add(clientFilter); + + String address = "https://localhost:" + PORT + "/encryptedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", "org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt"); + properties.put("rs.security.encryption.content.algorithm", "A128GCM"); + properties.put("rs.security.encryption.key.algorithm", "RSA1_5"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testWrongContentEncryptionAlgorithm() throws Exception { + if (!SecurityTestUtil.checkUnrestrictedPoliciesInstalled()) { + return; + } + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + JwtAuthenticationClientFilter clientFilter = new JwtAuthenticationClientFilter(); + clientFilter.setJwsRequired(false); + clientFilter.setJweRequired(true); + providers.add(clientFilter); + + String address = "https://localhost:" + PORT + "/encryptedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", "org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt"); + properties.put("rs.security.encryption.content.algorithm", "A128GCM"); + properties.put("rs.security.encryption.content.algorithm", "A192GCM"); + properties.put("rs.security.encryption.key.algorithm", "RSA-OAEP"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testBadEncryptingKey() throws Exception { + if (!SecurityTestUtil.checkUnrestrictedPoliciesInstalled()) { + return; + } + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + JwtAuthenticationClientFilter clientFilter = new JwtAuthenticationClientFilter(); + clientFilter.setJwsRequired(false); + clientFilter.setJweRequired(true); + providers.add(clientFilter); + + String address = "https://localhost:" + PORT + "/encryptedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "AliceCert"); + properties.put("rs.security.keystore.file", "org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt"); + properties.put("rs.security.encryption.content.algorithm", "A128GCM"); + properties.put("rs.security.encryption.key.algorithm", "RSA-OAEP"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + // + // Signature tests + // + + @org.junit.Test + public void testSignatureProperties() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.signature.properties", + "org/apache/cxf/systest/jaxrs/security/alice.jwk.properties"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testSignatureDynamic() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testWrongSignatureAlgorithm() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "PS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testBadSigningKey() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jks"); + properties.put("rs.security.keystore.password", "password"); + properties.put("rs.security.key.password", "password"); + properties.put("rs.security.keystore.alias", "alice"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/alice.jks"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testSignatureEllipticCurve() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwtec/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "ECKey"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "ES256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + // 1024 bits not allowed with RSA according to the spec + @org.junit.Test + public void testSmallSignatureKeySize() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jks"); + properties.put("rs.security.keystore.alias", "smallkey"); + properties.put("rs.security.keystore.password", "security"); + properties.put("rs.security.key.password", "security"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/smallkeysize.jks"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testUnsignedTokenSuccess() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/unsignedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.signature.algorithm", "none"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testUnsignedTokenFailure() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.signature.algorithm", "none"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testSignatureEncryptionProperties() throws Exception { + + URL busFile = JWTAlgorithmTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + JwtAuthenticationClientFilter clientFilter = new JwtAuthenticationClientFilter(); + clientFilter.setJwsRequired(true); + clientFilter.setJweRequired(true); + providers.add(clientFilter); + + String address = "https://localhost:" + PORT + "/signedencryptedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.signature.properties", + "org/apache/cxf/systest/jaxrs/security/alice.jwk.properties"); + properties.put("rs.security.encryption.properties", + "org/apache/cxf/systest/jaxrs/security/bob.jwk.properties"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } +} diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/PrivateKeyPasswordProviderImpl.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/PrivateKeyPasswordProviderImpl.java new file mode 100644 index 00000000000..bd577c65c4e --- /dev/null +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/PrivateKeyPasswordProviderImpl.java @@ -0,0 +1,40 @@ +/** + * 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.systest.jaxrs.security.jose.jwt; + +import java.util.Properties; + +import org.apache.cxf.rs.security.jose.common.PrivateKeyPasswordProvider; + +public class PrivateKeyPasswordProviderImpl implements PrivateKeyPasswordProvider { + + private String password = "password"; + public PrivateKeyPasswordProviderImpl() { + + } + public PrivateKeyPasswordProviderImpl(String password) { + this.password = password; + } + @Override + public char[] getPassword(Properties storeProperties) { + return password.toCharArray(); + } + +} + diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/algorithms-server.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/algorithms-server.xml new file mode 100644 index 00000000000..5e270ce3198 --- /dev/null +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/algorithms-server.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/client.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/client.xml new file mode 100644 index 00000000000..13eaea1a10c --- /dev/null +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/client.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + From f7f5b3c015ac82a65f144c5163f070aa594a3874 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 5 Nov 2015 17:14:05 +0000 Subject: [PATCH 0006/1346] [CXF-6664] Fixing NPE based on the input from with the help from Michael Krenn --- .../cxf/jaxrs/impl/LinkBuilderImpl.java | 19 ++++++++++++++++--- .../cxf/jaxrs/impl/LinkBuilderImplTest.java | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java index 5fda9d1218c..d35a2edf554 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java @@ -74,12 +74,25 @@ public Builder link(Link link) { @Override public Builder link(String link) { + + link = link.trim(); + if (link.length() > 1 && link.startsWith("<")) { + int index = link.indexOf(">", 1); + if (index != -1) { + String uri = link.substring(1, index); + ub = UriBuilder.fromUri(uri); + if (index + 1 == link.length()) { + link = ""; + } else { + link = link.substring(index + 1); + } + } + } + String[] tokens = StringUtils.split(link, ";"); for (String token : tokens) { String theToken = token.trim(); - if (theToken.startsWith("<") && theToken.endsWith(">")) { - ub = UriBuilder.fromUri(theToken.substring(1, theToken.length() - 1)); - } else { + if (!theToken.isEmpty()) { int i = theToken.indexOf('='); if (i != -1) { String name = theToken.substring(0, i); diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java index a3790dc430a..70920cc2531 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java @@ -41,7 +41,7 @@ public void testBuild() throws Exception { } @Test - public void testbBuildObjects() throws Exception { + public void testBuildObjects() throws Exception { StringBuilder path1 = new StringBuilder().append("p1"); ByteArrayInputStream path2 = new ByteArrayInputStream("p2".getBytes()) { @Override From 0d2a6ccabc752708484e2b2c44deb5b48e71b76e Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 5 Nov 2015 17:21:32 +0000 Subject: [PATCH 0007/1346] Some modifications to support implicit OIDC flows --- .../cxf/jaxrs/impl/ResponseImplTest.java | 4 +- .../cxf/jaxrs/impl/UriBuilderImplTest.java | 7 ++ .../provider/DefaultSubjectCreator.java | 34 ++++++++++ .../AbstractImplicitGrantService.java | 66 +++++++++++++------ .../oauth2/services/ImplicitGrantService.java | 5 ++ .../RedirectionBasedGrantService.java | 20 +++--- .../rs/security/oauth2/utils/OAuthUtils.java | 9 ++- .../oidc/idp/OidcImplicitService.java | 62 +++++++++++++++++ 8 files changed, 175 insertions(+), 32 deletions(-) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultSubjectCreator.java create mode 100644 rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java index b7505082dc1..d409072a1a9 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ResponseImplTest.java @@ -367,7 +367,7 @@ public void testGetLinks() { assertFalse(ri.hasLink("prev")); assertNull(ri.getLink("prev")); - meta.add(HttpHeaders.LINK, ";rel=next"); + meta.add(HttpHeaders.LINK, ";rel=next"); meta.add(HttpHeaders.LINK, ";rel=prev"); assertTrue(ri.hasLink("next")); @@ -381,7 +381,7 @@ public void testGetLinks() { assertTrue(links.contains(next)); assertTrue(links.contains(prev)); - assertEquals("http://next", next.getUri().toString()); + assertEquals("http://localhost:8080/next;a=b", next.getUri().toString()); assertEquals("next", next.getRel()); assertEquals("http://prev", prev.getUri().toString()); assertEquals("prev", prev.getRel()); diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java index a9ec84352ab..2bf78299aac 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java @@ -1546,6 +1546,13 @@ public void testPathParamSpaceBuild4() { assertEquals(expected, uri.toString()); } + @Test + public void testFromUriWithMatrix() { + String expected = "http://localhost:8080/name;a=b"; + URI uri = UriBuilder.fromUri("http://localhost:8080/name;a=b").build(); + assertEquals(expected, uri.toString()); + } + @Test public void testPathParamSpaceBuildEncoded() { String expected = "http://localhost:8080/name/%20"; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultSubjectCreator.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultSubjectCreator.java new file mode 100644 index 00000000000..ae870fb752a --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultSubjectCreator.java @@ -0,0 +1,34 @@ +/** + * 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.provider; + +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; +import org.apache.cxf.security.SecurityContext; + +public class DefaultSubjectCreator implements SubjectCreator { + + @Override + public UserSubject createUserSubject(MessageContext mc) throws OAuthServiceException { + return OAuthUtils.createSubject(mc, + (SecurityContext)mc.get(SecurityContext.class.getName())); + } + +} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java index 63fcfa2ce66..d78feafbf96 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java @@ -23,6 +23,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import javax.ws.rs.core.Response; @@ -48,6 +49,10 @@ protected AbstractImplicitGrantService(String supportedResponseType, String supportedGrantType) { super(supportedResponseType, supportedGrantType); } + protected AbstractImplicitGrantService(Set supportedResponseTypes, + String supportedGrantType) { + super(supportedResponseTypes, supportedGrantType); + } protected Response createGrant(OAuthRedirectionState state, Client client, @@ -55,43 +60,63 @@ protected Response createGrant(OAuthRedirectionState state, List approvedScope, UserSubject userSubject, ServerAccessToken preAuthorizedToken) { + + boolean tokenCanBeReturned = preAuthorizedToken != null; ServerAccessToken token = null; if (preAuthorizedToken == null) { - AccessTokenRegistration reg = new AccessTokenRegistration(); - reg.setClient(client); - reg.setGrantType(super.getSupportedGrantType()); - reg.setSubject(userSubject); - reg.setRequestedScope(requestedScope); - if (approvedScope != null && approvedScope.isEmpty()) { - // no down-scoping done by a user, all of the requested scopes have been authorized - reg.setApprovedScope(requestedScope); - } else { - reg.setApprovedScope(approvedScope); + tokenCanBeReturned = canAccessTokenBeReturned(requestedScope, approvedScope); + if (tokenCanBeReturned) { + AccessTokenRegistration reg = new AccessTokenRegistration(); + reg.setClient(client); + reg.setGrantType(super.getSupportedGrantType()); + reg.setSubject(userSubject); + reg.setRequestedScope(requestedScope); + if (approvedScope != null && approvedScope.isEmpty()) { + // no down-scoping done by a user, all of the requested scopes have been authorized + reg.setApprovedScope(requestedScope); + } else { + reg.setApprovedScope(approvedScope); + } + reg.setAudience(state.getAudience()); + token = getDataProvider().createAccessToken(reg); } - reg.setAudience(state.getAudience()); - token = getDataProvider().createAccessToken(reg); } else { token = preAuthorizedToken; } - ClientAccessToken clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters()); + ClientAccessToken clientToken = null; + if (token != null) { + clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters()); + } else { + // this is not ideal - it is only done to have OIDC Implicit to have an id_token added + // via AccessTokenResponseFilter. Note if id_token is needed (with or without access token) + // then the service needs to be injected with SubjectCreator, example, DefaultSubjectCreator + // extension which will have a chance to attach id_token to Subject properties which are checked + // by id_token AccessTokenResponseFilter. If at is also needed then OAuthDataProvider may deal + // with attaching id_token itself in which case no SubjectCreator injection is necessary + clientToken = new ClientAccessToken(); + } processClientAccessToken(clientToken, token); // return the token by appending it as a fragment parameter to the redirect URI StringBuilder sb = getUriWithFragment(state.getRedirectUri()); + if (tokenCanBeReturned) { + sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey()); + sb.append("&"); + sb.append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType()); + } - sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey()); if (state.getState() != null) { sb.append("&"); sb.append(OAuthConstants.STATE).append("=").append(state.getState()); } - sb.append("&") - .append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType()); if (isWriteOptionalParameters()) { - sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN) - .append("=").append(clientToken.getExpiresIn()); + if (tokenCanBeReturned) { + sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN) + .append("=").append(clientToken.getExpiresIn()); + } if (!StringUtils.isEmpty(clientToken.getApprovedScope())) { sb.append("&").append(OAuthConstants.SCOPE).append("=") .append(HttpUtils.queryEncode(clientToken.getApprovedScope())); @@ -100,7 +125,7 @@ protected Response createGrant(OAuthRedirectionState state, sb.append("&").append(entry.getKey()).append("=").append(HttpUtils.queryEncode(entry.getValue())); } } - if (token.getRefreshToken() != null) { + if (tokenCanBeReturned && token.getRefreshToken() != null) { processRefreshToken(sb, token.getRefreshToken()); } if (reportClientId) { @@ -109,6 +134,9 @@ protected Response createGrant(OAuthRedirectionState state, return Response.seeOther(URI.create(sb.toString())).build(); } + protected boolean canAccessTokenBeReturned(List requestedScope, List approvedScope) { + return true; + } protected void processRefreshToken(StringBuilder sb, String refreshToken) { LOG.warning("Implicit grant tokens MUST not have refresh tokens, refresh token will not be reported"); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java index a73e118736a..d2dcdbf1d35 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java @@ -19,6 +19,8 @@ package org.apache.cxf.rs.security.oauth2.services; +import java.util.Set; + import javax.ws.rs.Path; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; @@ -39,6 +41,9 @@ public class ImplicitGrantService extends AbstractImplicitGrantService { public ImplicitGrantService() { super(OAuthConstants.TOKEN_RESPONSE_TYPE, OAuthConstants.IMPLICIT_GRANT); } + public ImplicitGrantService(Set responseTypes) { + super(responseTypes, OAuthConstants.IMPLICIT_GRANT); + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index c1744293b9a..51ea97ec121 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -19,9 +19,11 @@ package org.apache.cxf.rs.security.oauth2.services; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -53,7 +55,7 @@ * The Base Redirection-Based Grant Service */ public abstract class RedirectionBasedGrantService extends AbstractOAuthService { - private String supportedResponseType; + private Set supportedResponseTypes; private String supportedGrantType; private boolean partialMatchScopeValidation; private boolean useRegisteredRedirectUriIfPossible = true; @@ -65,7 +67,11 @@ public abstract class RedirectionBasedGrantService extends AbstractOAuthService protected RedirectionBasedGrantService(String supportedResponseType, String supportedGrantType) { - this.supportedResponseType = supportedResponseType; + this(Collections.singleton(supportedResponseType), supportedGrantType); + } + protected RedirectionBasedGrantService(Set supportedResponseTypes, + String supportedGrantType) { + this.supportedResponseTypes = supportedResponseTypes; this.supportedGrantType = supportedGrantType; } @@ -131,7 +137,7 @@ protected Response startAuthorization(MultivaluedMap params, // Check response_type String responseType = params.getFirst(OAuthConstants.RESPONSE_TYPE); - if (responseType == null || !responseType.equals(supportedResponseType)) { + if (responseType == null || !supportedResponseTypes.contains(responseType)) { return createErrorResponse(params, redirectUri, OAuthConstants.UNSUPPORTED_RESPONSE_TYPE); } // Get the requested scopes @@ -324,13 +330,7 @@ protected UserSubject createUserSubject(SecurityContext securityContext) { return subject; } } - - subject = getMessageContext().getContent(UserSubject.class); - if (subject != null) { - return subject; - } else { - return OAuthUtils.createSubject(securityContext); - } + return OAuthUtils.createSubject(getMessageContext(), securityContext); } protected Response createErrorResponse(MultivaluedMap params, diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java index c96de4429c4..ad190dfb46e 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java @@ -91,7 +91,14 @@ public static String getSessionToken(MessageContext mc, String attribute, boolea } return sessionToken; } - + public static UserSubject createSubject(MessageContext mc, SecurityContext sc) { + UserSubject subject = mc.getContent(UserSubject.class); + if (subject != null) { + return subject; + } else { + return OAuthUtils.createSubject(sc); + } + } public static UserSubject createSubject(SecurityContext securityContext) { List roleNames = Collections.emptyList(); if (securityContext instanceof LoginSecurityContext) { diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java new file mode 100644 index 00000000000..c6638e3b2d2 --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java @@ -0,0 +1,62 @@ +/** + * 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.oidc.idp; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import javax.ws.rs.Path; + +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; +import org.apache.cxf.rs.security.oauth2.services.ImplicitGrantService; + +@Path("/login") +public class OidcImplicitService extends ImplicitGrantService { + private static final String OPEN_ID_CONNECT_SCOPE = "openid"; + private static final String ID_TOKEN_RESPONSE_TYPE = "id_token"; + private static final String ID_TOKEN_AND_AT_RESPONSE_TYPE = "id_token token"; + private boolean skipAuthorizationWithOidcScope; + + public OidcImplicitService() { + super(new HashSet(Arrays.asList(ID_TOKEN_RESPONSE_TYPE, + ID_TOKEN_AND_AT_RESPONSE_TYPE))); + } + + @Override + protected boolean canAccessTokenBeReturned(List requestedScope, List approvedScope) { + return requestedScope.contains(ID_TOKEN_AND_AT_RESPONSE_TYPE); + } + + @Override + protected boolean canAuthorizationBeSkipped(Client client, + List requestedScope, + List permissions) { + // No need to challenge the authenticated user with the authorization form + // if all the client application redirecting a user needs is to get this user authenticated + // with OIDC IDP + return requestedScope.size() == 1 && permissions.size() == 1 && skipAuthorizationWithOidcScope + && OPEN_ID_CONNECT_SCOPE.equals(requestedScope.get(0)); + } + public void setSkipAuthorizationWithOidcScope(boolean skipAuthorizationWithOidcScope) { + this.skipAuthorizationWithOidcScope = skipAuthorizationWithOidcScope; + } + +} From a9d135b012e50e0e558d3c5c797efe30790de2f2 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 6 Nov 2015 10:50:50 +0000 Subject: [PATCH 0008/1346] [CXF-6668] Fixing the case of independent query parameters assumed to be multipart params, patch from Alexei Marchenko applied with thanks --- .../tools/wadlto/jaxrs/SourceGenerator.java | 7 ++--- .../wadlto/jaxrs/JAXRSContainerTest.java | 25 ++++++++++++++++++ .../wadl/testQueryMultipartParam.wadl | 26 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 tools/wadlto/jaxrs/src/test/resources/wadl/testQueryMultipartParam.wadl diff --git a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java index c8e034d688d..285f85fe5bd 100644 --- a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java +++ b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java @@ -1201,15 +1201,16 @@ private void writeRequestTypes(Element requestEl, boolean multipart = false; boolean formOrMultipartParamsAvailable = false; String requestMediaType = null; + int currentSize = 0; if (requestEl != null) { inParamEls.addAll(getWadlElements(requestEl, "param")); - int currentSize = inParamEls.size(); + currentSize = inParamEls.size(); List repElements = getWadlElements(requestEl, "representation"); form = addFormParameters(inParamEls, requestEl, repElements); if (form) { formOrMultipartParamsAvailable = currentSize < inParamEls.size(); requestMediaType = repElements.get(0).getAttribute("mediaType"); - multipart = form && requestMediaType.startsWith("multipart/"); + multipart = requestMediaType.startsWith("multipart/"); } } @@ -1218,7 +1219,7 @@ private void writeRequestTypes(Element requestEl, Element paramEl = inParamEls.get(i); Class paramAnn = getParamAnnotation(paramEl.getAttribute("style")); - if (paramAnn == QueryParam.class && formOrMultipartParamsAvailable) { + if (i >= currentSize && paramAnn == QueryParam.class && formOrMultipartParamsAvailable) { paramAnn = !multipart ? FormParam.class : Multipart.class; } String name = paramEl.getAttribute("name"); diff --git a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java index 7c3e82629d3..e4ad62dd040 100644 --- a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java +++ b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java @@ -314,6 +314,31 @@ public void testResourceWithEPRNoSchemaGen() { } } + @Test + public void testQueryMultipartParam() { + try { + JAXRSContainer container = new JAXRSContainer(null); + + ToolContext context = new ToolContext(); + context.put(WadlToolConstants.CFG_OUTPUTDIR, output.getCanonicalPath()); + context.put(WadlToolConstants.CFG_WADLURL, getLocation("/wadl/testQueryMultipartParam.wadl")); + context.put(WadlToolConstants.CFG_COMPILE, "true"); + + container.setContext(context); + container.execute(); + + assertNotNull(output.list()); + + List files = FileUtils.getFilesRecurse(output, ".+\\." + "class" + "$"); + assertEquals(2, files.size()); + assertTrue(checkContains(files, "application.Test1.class")); + assertTrue(checkContains(files, "application.Test2.class")); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + @Test public void testCodeGenWithImportedSchemaAndResourceSet() { try { diff --git a/tools/wadlto/jaxrs/src/test/resources/wadl/testQueryMultipartParam.wadl b/tools/wadlto/jaxrs/src/test/resources/wadl/testQueryMultipartParam.wadl new file mode 100644 index 00000000000..914e68f8020 --- /dev/null +++ b/tools/wadlto/jaxrs/src/test/resources/wadl/testQueryMultipartParam.wadl @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From 57acdfc00c5c8d749cab353cabd31367447f0e66 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 6 Nov 2015 12:06:43 +0000 Subject: [PATCH 0009/1346] [CXF-6661] Optional link to a WADL schema location --- .../maven_plugin/javatowadl/Java2WADLMojo.java | 6 ++++++ .../cxf/jaxrs/model/wadl/WadlGenerator.java | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/maven-plugins/java2wadl-plugin/src/main/java/org/apache/cxf/maven_plugin/javatowadl/Java2WADLMojo.java b/maven-plugins/java2wadl-plugin/src/main/java/org/apache/cxf/maven_plugin/javatowadl/Java2WADLMojo.java index 75e63a31be9..dd1fb262bc1 100644 --- a/maven-plugins/java2wadl-plugin/src/main/java/org/apache/cxf/maven_plugin/javatowadl/Java2WADLMojo.java +++ b/maven-plugins/java2wadl-plugin/src/main/java/org/apache/cxf/maven_plugin/javatowadl/Java2WADLMojo.java @@ -139,6 +139,11 @@ public class Java2WADLMojo extends AbstractMojo { */ private boolean useSingleSlashResource; + /** + * @parameter default-value="false" + */ + private boolean includeDefaultWadlSchemaLocation; + /** * @parameter default-value="false" */ @@ -242,6 +247,7 @@ public void execute() throws MojoExecutionException { private void setExtraProperties(WadlGenerator wg) { wg.setSingleResourceMultipleMethods(singleResourceMultipleMethods); wg.setUseSingleSlashResource(useSingleSlashResource); + wg.setIncludeDefaultWadlSchemaLocation(includeDefaultWadlSchemaLocation); wg.setIgnoreForwardSlash(ignoreForwardSlash); wg.setAddResourceAndMethodIds(addResourceAndMethodIds); wg.setLinkAnyMediaTypeToXmlSchema(linkAnyMediaTypeToXmlSchema); diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java index 76159a76515..d4ccd83eee0 100644 --- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java @@ -135,6 +135,7 @@ public class WadlGenerator implements ContainerRequestFilter { public static final String WADL_QUERY = "_wadl"; public static final MediaType WADL_TYPE = JAXRSUtils.toMediaType("application/vnd.sun.wadl+xml"); public static final String WADL_NS = "http://wadl.dev.java.net/2009/02"; + public static final String DEFAULT_WADL_SCHEMA_LOC = "http://www.w3.org/Submission/wadl/wadl.xsd"; private static final Logger LOG = LogUtils.getL7dLogger(WadlGenerator.class); private static final String CONVERT_WADL_RESOURCES_TO_DOM = "convert.wadl.resources.to.dom"; @@ -172,7 +173,7 @@ public class WadlGenerator implements ContainerRequestFilter { private boolean ignoreMessageWriters = true; private boolean ignoreRequests; private boolean convertResourcesToDOM = true; - + private String wadlSchemaLocation; private List externalSchemasCache; private List externalSchemaLinks; private Map> externalQnamesMap; @@ -374,6 +375,13 @@ protected void handleGrammars(StringBuilder sbApp, StringBuilder sbGrammars, Sch .append("\""); } + if (wadlSchemaLocation != null) { + sbApp.append(" xmlns:xsi=\"").append(Constants.URI_2001_SCHEMA_XSI).append("\""); + sbApp.append(" xsi:schemaLocation=\"") + .append(getNamespace()).append(" ").append(wadlSchemaLocation) + .append("\""); + } + writer.write(sbGrammars); } @@ -2156,6 +2164,14 @@ public void setDocumentationProvider(List ps) { public void setStylesheetReference(String stylesheetReference) { this.stylesheetReference = stylesheetReference; } + public void setIncludeWadlSchemaLocation(String loc) { + this.wadlSchemaLocation = loc; + } + public void setIncludeDefaultWadlSchemaLocation(boolean inc) { + if (inc) { + setIncludeWadlSchemaLocation(DEFAULT_WADL_SCHEMA_LOC); + } + } public void setIgnoreOverloadedMethods(boolean ignore) { this.ignoreOverloadedMethods = ignore; From dc88f7385af5c26aba31c74c1010265567615002 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 6 Nov 2015 12:08:51 +0000 Subject: [PATCH 0010/1346] [CXF-6661] Minor update --- .../java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java index d4ccd83eee0..dd044fe6c87 100644 --- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java @@ -2164,12 +2164,12 @@ public void setDocumentationProvider(List ps) { public void setStylesheetReference(String stylesheetReference) { this.stylesheetReference = stylesheetReference; } - public void setIncludeWadlSchemaLocation(String loc) { + public void setWadlSchemaLocation(String loc) { this.wadlSchemaLocation = loc; } public void setIncludeDefaultWadlSchemaLocation(boolean inc) { if (inc) { - setIncludeWadlSchemaLocation(DEFAULT_WADL_SCHEMA_LOC); + setWadlSchemaLocation(DEFAULT_WADL_SCHEMA_LOC); } } From 52a6c7344a89eccca94c92475e0e5651f7867c0a Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 12:19:01 +0000 Subject: [PATCH 0011/1346] Allow setting the signature properties directly --- .../jose/common/KeyManagementUtils.java | 2 +- .../security/jose/jws/JwsCompactProducer.java | 22 ++++++++++++++----- .../cxf/rs/security/jose/jws/JwsUtils.java | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java index bb771a6c720..a53e7a8c574 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -311,7 +311,7 @@ public static X509Certificate[] toX509CertificateChainArray(List base64E } public static String getKeyAlgorithm(Message m, Properties props, String propName, String defaultAlg) { String algo = props.getProperty(propName); - if (algo == null) { + if (algo == null && m != null) { algo = (String)m.getContextualProperty(propName); } if (algo == null) { diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java index 8ed3dc6104a..5fba6359248 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java @@ -37,6 +37,7 @@ public class JwsCompactProducer { private String plainJwsPayload; private String signature; private boolean detached; + private Properties signatureProperties; public JwsCompactProducer(String plainJwsPayload) { this(plainJwsPayload, false); } @@ -138,12 +139,10 @@ private SignatureAlgorithm getAlgorithm() { return getJwsHeaders().getSignatureAlgorithm(); } private void checkAlgorithm() { - if (getAlgorithm() == null && PhaseInterceptorChain.getCurrentMessage() != null) { + if (getAlgorithm() == null) { + Properties sigProps = getSignatureProperties(); Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, false, - JoseConstants.RSSEC_SIGNATURE_OUT_PROPS, - JoseConstants.RSSEC_SIGNATURE_PROPS); - String signatureAlgo = JwsUtils.getSignatureAlgo(m, props, null, null); + String signatureAlgo = JwsUtils.getSignatureAlgo(m, sigProps, null, null); if (signatureAlgo != null) { getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.getAlgorithm(signatureAlgo)); } @@ -153,4 +152,17 @@ private void checkAlgorithm() { throw new JwsException(JwsException.Error.INVALID_ALGORITHM); } } + public Properties getSignatureProperties() { + if (signatureProperties == null && PhaseInterceptorChain.getCurrentMessage() != null) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + signatureProperties = KeyManagementUtils.loadStoreProperties(m, false, + JoseConstants.RSSEC_SIGNATURE_OUT_PROPS, + JoseConstants.RSSEC_SIGNATURE_PROPS); + + } + return signatureProperties; + } + public void setSignatureProperties(Properties signatureProperties) { + this.signatureProperties = signatureProperties; + } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index fcebd8495ed..b9f0001a9ef 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -400,7 +400,7 @@ public static String getSignatureAlgo(Message m, Properties props, String algo, // Check for deprecated identifier first String sigAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); - if (sigAlgo == null) { + if (sigAlgo == null && m != null) { sigAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); } if (sigAlgo != null) { From a493fc41c1bdd5d282dac7fec57db9d01987af21 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 12:19:14 +0000 Subject: [PATCH 0012/1346] Adding initial TokenProvider to issue JWT tokens in the STS --- services/sts/sts-core/pom.xml | 6 + .../sts/operation/TokenIssueOperation.java | 7 +- .../token/provider/TokenProviderResponse.java | 8 +- .../jwt/DefaultJWTClaimsProvider.java | 130 ++++++++++++ .../token/provider/jwt/JWTClaimsProvider.java | 33 +++ .../jwt/JWTClaimsProviderParameters.java | 39 ++++ .../token/provider/jwt/JWTTokenProvider.java | 195 ++++++++++++++++++ .../mapper/JexlIssueSamlClaimsTest.java | 2 +- .../cxf/sts/operation/CancelSCTUnitTest.java | 2 +- .../cxf/sts/operation/IssueJWTUnitTest.java | 193 +++++++++++++++++ .../operation/IssueOnbehalfofUnitTest.java | 2 +- .../operation/IssueSamlClaimsUnitTest.java | 2 +- .../cxf/sts/operation/RenewSamlUnitTest.java | 2 +- .../sts/operation/ValidateSCTUnitTest.java | 2 +- .../sts/operation/ValidateSamlUnitTest.java | 2 +- .../ValidateTokenTransformationUnitTest.java | 2 +- .../token/provider/JWTTokenProviderTest.java | 124 +++++++++++ .../sts/token/provider/SAMLClaimsTest.java | 12 +- .../token/provider/SAMLProviderActAsTest.java | 10 +- .../provider/SAMLProviderCustomTest.java | 16 +- .../provider/SAMLProviderKeyTypeTest.java | 34 +-- .../provider/SAMLProviderLifetimeTest.java | 12 +- .../provider/SAMLProviderOnBehalfOfTest.java | 10 +- .../token/provider/SAMLProviderRealmTest.java | 6 +- .../sts/token/provider/SCTProviderTest.java | 6 +- .../renewer/SAMLTokenRenewerLifetimeTest.java | 2 +- .../renewer/SAMLTokenRenewerPOPTest.java | 2 +- .../renewer/SAMLTokenRenewerRealmTest.java | 2 +- .../token/renewer/SAMLTokenRenewerTest.java | 2 +- .../SAMLTokenValidatorCachedRealmTest.java | 2 +- .../SAMLTokenValidatorRealmTest.java | 2 +- .../validator/SAMLTokenValidatorTest.java | 8 +- 32 files changed, 800 insertions(+), 77 deletions(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProvider.java create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java diff --git a/services/sts/sts-core/pom.xml b/services/sts/sts-core/pom.xml index 046a8283f30..2d29ea191d1 100644 --- a/services/sts/sts-core/pom.xml +++ b/services/sts/sts-core/pom.xml @@ -49,6 +49,12 @@ ${project.version} compile + + org.apache.cxf + cxf-rt-rs-security-jose + ${project.version} + compile + net.sf.ehcache ehcache diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java index 903737eabe0..1d0c378200c 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java @@ -31,6 +31,8 @@ import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; +import org.w3c.dom.Element; + import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.rt.security.claims.ClaimCollection; @@ -286,9 +288,12 @@ private RequestSecurityTokenResponseType createResponse( if (!encryptIssuedToken) { requestedTokenType.setAny(tokenResponse.getToken()); } else { + if (!(tokenResponse.getToken() instanceof Element)) { + throw new STSException("Error in creating the response", STSException.REQUEST_FAILED); + } requestedTokenType.setAny( encryptToken( - tokenResponse.getToken(), tokenResponse.getTokenId(), + (Element)tokenResponse.getToken(), tokenResponse.getTokenId(), encryptionProperties, keyRequirements, webServiceContext ) ); diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderResponse.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderResponse.java index 3dda2e5f629..bf28778901f 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderResponse.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderResponse.java @@ -21,14 +21,12 @@ import java.util.Date; -import org.w3c.dom.Element; - /** * This class encapsulates the response from a TokenProvider instance after creating a token. */ public class TokenProviderResponse { - private Element token; + private Object token; private String tokenId; private byte[] entropy; private long keySize; @@ -70,7 +68,7 @@ public void setKeySize(long keySize) { * Set the token * @param token the token to set */ - public void setToken(Element token) { + public void setToken(Object token) { this.token = token; } @@ -78,7 +76,7 @@ public void setToken(Element token) { * Get the token * @return the token to set */ - public Element getToken() { + public Object getToken() { return token; } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java new file mode 100644 index 00000000000..5addb95df4c --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java @@ -0,0 +1,130 @@ +/** + * 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.sts.token.provider.jwt; + +import java.security.Principal; +import java.util.Date; +import java.util.UUID; +import java.util.logging.Logger; + +import javax.security.auth.x500.X500Principal; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.sts.request.ReceivedToken; +import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.ws.security.sts.provider.STSException; + +/** + * A default implementation to create a JWTClaims object. The Subject name is the name + * of the current principal. + */ +public class DefaultJWTClaimsProvider implements JWTClaimsProvider { + + private static final Logger LOG = LogUtils.getL7dLogger(DefaultJWTClaimsProvider.class); + private boolean useX500CN; + + /** + * Get a JwtClaims object. + */ + public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters) { + + JwtClaims claims = new JwtClaims(); + claims.setSubject(getSubjectName(jwtClaimsProviderParameters)); + claims.setTokenId(UUID.randomUUID().toString()); + claims.setIssuer("DoubleItSTSIssuer"); + + Date currentDate = new Date(); + claims.setIssuedAt(currentDate.getTime() / 1000L); + long currentTime = currentDate.getTime() + 300L * 1000L; + currentDate.setTime(currentTime); + claims.setExpiryTime(currentDate.getTime() / 1000L); + + return claims; + } + + protected String getSubjectName(JWTClaimsProviderParameters jwtClaimsProviderParameters) { + Principal principal = getPrincipal(jwtClaimsProviderParameters); + if (principal == null) { + LOG.fine("Error in getting principal"); + throw new STSException("Error in getting principal", STSException.REQUEST_FAILED); + } + + String subjectName = principal.getName(); + if (principal instanceof X500Principal) { + // Just use the "cn" instead of the entire DN + try { + String principalName = principal.getName(); + int index = principalName.indexOf('='); + principalName = principalName.substring(index + 1, principalName.indexOf(',', index)); + subjectName = principalName; + } catch (Throwable ex) { + subjectName = principal.getName(); + //Ignore, not X500 compliant thus use the whole string as the value + } + } + + return subjectName; + } + + /** + * Get the Principal (which is used as the Subject). By default, we check the following (in order): + * - A valid OnBehalfOf principal + * - A valid ActAs principal + * - A valid principal associated with a token received as ValidateTarget + * - The principal associated with the request. We don't need to check to see if it is "valid" here, as it + * is not parsed by the STS (but rather the WS-Security layer). + */ + protected Principal getPrincipal(JWTClaimsProviderParameters jwtClaimsProviderParameters) { + TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters(); + + Principal principal = null; + //TokenValidator in IssueOperation has validated the ReceivedToken + //if validation was successful, the principal was set in ReceivedToken + if (providerParameters.getTokenRequirements().getOnBehalfOf() != null) { + ReceivedToken receivedToken = providerParameters.getTokenRequirements().getOnBehalfOf(); + if (receivedToken.getState().equals(STATE.VALID)) { + principal = receivedToken.getPrincipal(); + } + } else if (providerParameters.getTokenRequirements().getActAs() != null) { + ReceivedToken receivedToken = providerParameters.getTokenRequirements().getActAs(); + if (receivedToken.getState().equals(STATE.VALID)) { + principal = receivedToken.getPrincipal(); + } + } else if (providerParameters.getTokenRequirements().getValidateTarget() != null) { + ReceivedToken receivedToken = providerParameters.getTokenRequirements().getValidateTarget(); + if (receivedToken.getState().equals(STATE.VALID)) { + principal = receivedToken.getPrincipal(); + } + } else { + principal = providerParameters.getPrincipal(); + } + + return principal; + } + + public boolean isUseX500CN() { + return useX500CN; + } + + public void setUseX500CN(boolean useX500CN) { + this.useX500CN = useX500CN; + } +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProvider.java new file mode 100644 index 00000000000..1505e60f8ff --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProvider.java @@ -0,0 +1,33 @@ +/** + * 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.sts.token.provider.jwt; + +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; + +/** + * An interface that allows a pluggable way of creating a JWTClaims object + */ +public interface JWTClaimsProvider { + + /** + * Get a JwtClaims object. + */ + JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters); + +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java new file mode 100644 index 00000000000..24f1ed9f999 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java @@ -0,0 +1,39 @@ +/** + * 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.sts.token.provider.jwt; + +import org.apache.cxf.sts.token.provider.TokenProviderParameters; + +/** + * The parameters that are passed through to a JWTClaimsProvider implementation to create a + * JWTClaims Object. + */ +public class JWTClaimsProviderParameters { + + private TokenProviderParameters providerParameters; + + public TokenProviderParameters getProviderParameters() { + return providerParameters; + } + + public void setProviderParameters(TokenProviderParameters providerParameters) { + this.providerParameters = providerParameters; + } + +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java new file mode 100644 index 00000000000..b4582811bbe --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -0,0 +1,195 @@ +/** + * 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.sts.token.provider.jwt; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.ws.security.sts.provider.STSException; + +/** + * A TokenProvider implementation that provides a JWT Token. + */ +public class JWTTokenProvider implements TokenProvider { + + public static final String JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"; + private static final Logger LOG = LogUtils.getL7dLogger(JWTTokenProvider.class); + + private boolean signToken = true; + private Map realmMap = new HashMap<>(); + private JWTClaimsProvider jwtClaimsProvider = new DefaultJWTClaimsProvider(); + + /** + * Return true if this TokenProvider implementation is capable of providing a token + * that corresponds to the given TokenType. + */ + public boolean canHandleToken(String tokenType) { + return canHandleToken(tokenType, null); + } + + /** + * Return true if this TokenProvider implementation is capable of providing a token + * that corresponds to the given TokenType in a given realm. + */ + public boolean canHandleToken(String tokenType, String realm) { + if (realm != null && !realmMap.containsKey(realm)) { + return false; + } + return JWT_TOKEN_TYPE.equals(tokenType); + } + + /** + * Create a token given a TokenProviderParameters + */ + public TokenProviderResponse createToken(TokenProviderParameters tokenParameters) { + //KeyRequirements keyRequirements = tokenParameters.getKeyRequirements(); + TokenRequirements tokenRequirements = tokenParameters.getTokenRequirements(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Handling token of type: " + tokenRequirements.getTokenType()); + } + + // Get the claims + JWTClaimsProviderParameters jwtClaimsProviderParameters = new JWTClaimsProviderParameters(); + jwtClaimsProviderParameters.setProviderParameters(tokenParameters); + + JwtClaims claims = jwtClaimsProvider.getJwtClaims(jwtClaimsProviderParameters); + + /* + if (signToken) { + STSPropertiesMBean stsProperties = tokenParameters.getStsProperties(); + signToken(assertion, samlRealm, stsProperties, tokenParameters.getKeyRequirements()); + } + */ + + try { + /* + Document doc = DOMUtils.createDocument(); + SamlAssertionWrapper assertion = createSamlToken(tokenParameters, secret, doc); + Element token = assertion.toDOM(doc); + + // set the token in cache (only if the token is signed) + byte[] signatureValue = assertion.getSignatureValue(); + if (tokenParameters.getTokenStore() != null && signatureValue != null + && signatureValue.length > 0) { + DateTime validTill = null; + if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)) { + validTill = assertion.getSaml2().getConditions().getNotOnOrAfter(); + } else { + validTill = assertion.getSaml1().getConditions().getNotOnOrAfter(); + } + + SecurityToken securityToken = + CacheUtils.createSecurityTokenForStorage(token, assertion.getId(), + validTill.toDate(), tokenParameters.getPrincipal(), tokenParameters.getRealm(), + tokenParameters.getTokenRequirements().getRenewing()); + CacheUtils.storeTokenInCache( + securityToken, tokenParameters.getTokenStore(), signatureValue); + } + */ + + JwtToken token = new JwtToken(claims); + + Properties signingProperties = new Properties(); + signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, "none"); + + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token); + jws.setSignatureProperties(signingProperties); + String tokenData = jws.getSignedEncodedJws(); + + TokenProviderResponse response = new TokenProviderResponse(); + response.setToken(tokenData); + + response.setTokenId(claims.getTokenId()); + + if (claims.getIssuedAt() > 0) { + response.setCreated(new Date(claims.getIssuedAt() * 1000L)); + } + if (claims.getExpiryTime() > 0) { + response.setExpires(new Date(claims.getExpiryTime() * 1000L)); + } + + /*response.setEntropy(entropyBytes); + if (keySize > 0) { + response.setKeySize(keySize); + } + response.setComputedKey(computedKey); + */ + LOG.fine("JWT Token successfully created"); + return response; + } catch (Exception e) { + e.printStackTrace(); + LOG.log(Level.WARNING, "", e); + throw new STSException("Can't serialize JWT token", e, STSException.REQUEST_FAILED); + } + } + + /** + * Return whether the provided token will be signed or not. Default is true. + */ + public boolean isSignToken() { + return signToken; + } + + /** + * Set whether the provided token will be signed or not. Default is true. + */ + public void setSignToken(boolean signToken) { + this.signToken = signToken; + } + + /** + * Set the map of realm->SAMLRealm for this token provider + * @param realms the map of realm->SAMLRealm for this token provider + */ + public void setRealmMap(Map realms) { + this.realmMap = realms; + } + + /** + * Get the map of realm->SAMLRealm for this token provider + * @return the map of realm->SAMLRealm for this token provider + */ + public Map getRealmMap() { + return realmMap; + } + + public JWTClaimsProvider getJwtClaimsProvider() { + return jwtClaimsProvider; + } + + public void setJwtClaimsProvider(JWTClaimsProvider jwtClaimsProvider) { + this.jwtClaimsProvider = jwtClaimsProvider; + } + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java index 50cb9fd9954..f43ba17cbbc 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java @@ -358,7 +358,7 @@ private Element createSAMLAssertion(String tokenType, Crypto crypto, String sign assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters(String tokenType, String keyType, Crypto crypto, diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/CancelSCTUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/CancelSCTUnitTest.java index bc0990fceca..f8dc0cf23b3 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/CancelSCTUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/CancelSCTUnitTest.java @@ -98,7 +98,7 @@ public void testCancelSCT() throws Exception { // Get a SecurityContextToken via the SCTProvider TokenProviderResponse providerResponse = createSCT(); - Element sct = providerResponse.getToken(); + Element sct = (Element)providerResponse.getToken(); Document doc = sct.getOwnerDocument(); sct = (Element)doc.appendChild(sct); CancelTargetType cancelTarget = new CancelTargetType(); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java new file mode 100644 index 00000000000..6112d2f81af --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java @@ -0,0 +1,193 @@ +/** + * 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.sts.operation; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.service.ServiceMBean; +import org.apache.cxf.sts.service.StaticService; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; +import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; + +/** + * Some unit tests for the issue operation to issue JWT Tokens. + */ +public class IssueJWTUnitTest extends org.junit.Assert { + + public static final QName REQUESTED_SECURITY_TOKEN = + QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(null).getName(); + public static final QName ATTACHED_REFERENCE = + QNameConstants.WS_TRUST_FACTORY.createRequestedAttachedReference(null).getName(); + public static final QName UNATTACHED_REFERENCE = + QNameConstants.WS_TRUST_FACTORY.createRequestedUnattachedReference(null).getName(); + + private static TokenStore tokenStore = new DefaultInMemoryTokenStore(); + + /** + * Test to successfully issue a JWT Token + */ + @org.junit.Test + public void testIssueJWTToken() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + issueOperation.setTokenStore(tokenStore); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new JWTTokenProvider()); + issueOperation.setTokenProviders(providerList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + issueOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + jwtToken = (String)rstType.getAny(); + break; + } + } + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + /* + * Create a security context object + */ + private SecurityContext createSecurityContext(final Principal p) { + return new SecurityContext() { + public Principal getUserPrincipal() { + return p; + } + public boolean isUserInRole(String role) { + return false; + } + }; + } + + /* + * Mock up an AppliesTo element using the supplied address + */ + private Element createAppliesToElement(String addressUrl) { + Document doc = DOMUtils.createDocument(); + Element appliesTo = doc.createElementNS(STSConstants.WSP_NS, "wsp:AppliesTo"); + appliesTo.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsp", STSConstants.WSP_NS); + Element endpointRef = doc.createElementNS(STSConstants.WSA_NS_05, "wsa:EndpointReference"); + endpointRef.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsa", STSConstants.WSA_NS_05); + Element address = doc.createElementNS(STSConstants.WSA_NS_05, "wsa:Address"); + address.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsa", STSConstants.WSA_NS_05); + address.setTextContent(addressUrl); + endpointRef.appendChild(address); + appliesTo.appendChild(endpointRef); + return appliesTo; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java index f77e4c818cd..6a2354d742f 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java @@ -1240,7 +1240,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java index 7c4ae20ea05..acd061ab032 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java @@ -878,7 +878,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/RenewSamlUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/RenewSamlUnitTest.java index b60099ea3c1..1d50ff868e7 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/RenewSamlUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/RenewSamlUnitTest.java @@ -516,7 +516,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSCTUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSCTUnitTest.java index 55d142d7aec..d17404b823a 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSCTUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSCTUnitTest.java @@ -105,7 +105,7 @@ public void testValidateSCT() throws Exception { // Get a SecurityContextToken via the SCTProvider TokenProviderResponse providerResponse = createSCT(); - Element sct = providerResponse.getToken(); + Element sct = (Element)providerResponse.getToken(); Document doc = sct.getOwnerDocument(); sct = (Element)doc.appendChild(sct); ValidateTargetType validateTarget = new ValidateTargetType(); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSamlUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSamlUnitTest.java index eb9be37af39..53ade107491 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSamlUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateSamlUnitTest.java @@ -247,7 +247,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java index c0d4f3d00d5..857ea55e7a5 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java @@ -871,7 +871,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java new file mode 100644 index 00000000000..19d41f218a4 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java @@ -0,0 +1,124 @@ +/** + * 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.sts.token.provider; + +import java.util.Properties; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.junit.Assert; + +/** + * Some unit tests for creating JWTTokens. + */ +public class JWTTokenProviderTest extends org.junit.Assert { + + private static TokenStore tokenStore = new DefaultInMemoryTokenStore(); + + @org.junit.Test + public void testCreateUnsignedJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 2); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + } + + private TokenProviderParameters createProviderParameters() throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(JWTTokenProvider.JWT_TOKEN_TYPE); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setTokenStore(tokenStore); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java index 9aa376b4f77..f4d292bc38e 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java @@ -100,7 +100,7 @@ public void testSaml2Claims() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -145,7 +145,7 @@ public void testSaml2MultipleClaims() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -194,7 +194,7 @@ public void testSaml2MultipleClaimsSameDialect() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -238,7 +238,7 @@ public void testSaml2StaticClaims() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -295,7 +295,7 @@ public void testSaml2StaticEndpointClaims() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -387,7 +387,7 @@ public void testSaml2ClaimsInteger() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderActAsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderActAsTest.java index 48a42633074..b032602ee6b 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderActAsTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderActAsTest.java @@ -81,7 +81,7 @@ public void testDefaultSaml1ActAsUsernameToken() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -112,7 +112,7 @@ public void testDefaultSaml2ActAsAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -149,7 +149,7 @@ public void testCustomHandlingUsernameToken() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -170,7 +170,7 @@ public void testCustomHandlingUsernameToken() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains("CustomActAs")); } @@ -184,7 +184,7 @@ private Element getSAMLAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderCustomTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderCustomTest.java index 07fa6769f4f..9ead2803b45 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderCustomTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderCustomTest.java @@ -66,7 +66,7 @@ public void testCustomSaml1AttributeAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -94,7 +94,7 @@ public void testCustomSaml2AuthenticationAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertFalse(tokenString.contains("AttributeStatement")); @@ -122,7 +122,7 @@ public void testCustomSaml1AuthenticationAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertFalse(tokenString.contains("AttributeStatement")); @@ -155,7 +155,7 @@ public void testCustomSaml2CombinedAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -182,7 +182,7 @@ public void testCustomSaml1MultipleAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -210,7 +210,7 @@ public void testCustomSaml2AuthDecisionAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertFalse(tokenString.contains("AttributeStatement")); @@ -235,7 +235,7 @@ public void testCustomSaml1SubjectAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -262,7 +262,7 @@ public void testCustomSaml1SubjectNameIDFormat() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderKeyTypeTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderKeyTypeTest.java index 25c2305b7f3..ec90777190b 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderKeyTypeTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderKeyTypeTest.java @@ -69,7 +69,7 @@ public void testDefaultSaml1BearerAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -92,7 +92,7 @@ public void testDefaultSaml2BearerAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -133,7 +133,7 @@ public void testDefaultSaml1PublicKeyAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -173,7 +173,7 @@ public void testDefaultSaml2PublicKeyAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -221,7 +221,7 @@ public void testDefaultSaml1SymmetricKeyAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -268,7 +268,7 @@ public void testDefaultSaml1SymmetricKeyAssertionSecretKey() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -320,7 +320,7 @@ public void testDefaultSaml2SymmetricKeyAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -389,7 +389,7 @@ public void testDefaultSaml1BearerKeyValueAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -417,7 +417,7 @@ public void testDefaultSaml2BearerUnsignedAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -446,7 +446,7 @@ public void testDefaultSaml1BearerAssertionPKCS12() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -473,7 +473,7 @@ public void testDefaultSaml2BearerDifferentC14nAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertFalse(tokenString.contains(WSConstants.C14N_EXCL_WITH_COMMENTS)); assertTrue(tokenString.contains(WSConstants.C14N_EXCL_OMIT_COMMENTS)); @@ -491,7 +491,7 @@ public void testDefaultSaml2BearerDifferentC14nAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(WSConstants.C14N_EXCL_WITH_COMMENTS)); } @@ -515,7 +515,7 @@ public void testDefaultSaml2BearerDifferentSignatureAlgorithm() throws Exception assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")); @@ -528,7 +528,7 @@ public void testDefaultSaml2BearerDifferentSignatureAlgorithm() throws Exception assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertFalse(tokenString.contains(signatureAlgorithm)); assertTrue(tokenString.contains("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")); @@ -542,7 +542,7 @@ public void testDefaultSaml2BearerDifferentSignatureAlgorithm() throws Exception assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(signatureAlgorithm)); } @@ -561,7 +561,7 @@ public void testDefaultSaml2BearerDifferentSignatureDigestAlgorithm() throws Exc assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(WSConstants.SHA256)); @@ -574,7 +574,7 @@ public void testDefaultSaml2BearerDifferentSignatureDigestAlgorithm() throws Exc assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(WSConstants.SHA1)); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderLifetimeTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderLifetimeTest.java index 1a9d38e3b53..e186afae15e 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderLifetimeTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderLifetimeTest.java @@ -81,7 +81,7 @@ public void testSaml2ValidLifetime() throws Exception { assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); assertEquals(requestedLifetime * 1000L, providerResponse.getExpires().getTime() - providerResponse.getCreated().getTime()); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); } @@ -112,7 +112,7 @@ public void testSaml2ProviderLifetime() throws Exception { assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); assertEquals(providerLifetime * 1000L, providerResponse.getExpires().getTime() - providerResponse.getCreated().getTime()); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); } @@ -234,7 +234,7 @@ public void testSaml2ExceededConfiguredMaxLifetimeButUpdated() throws Exception assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); assertEquals(maxLifetime * 1000L, providerResponse.getExpires().getTime() - providerResponse.getCreated().getTime()); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); } @@ -274,7 +274,7 @@ public void testSaml2NearFutureCreatedLifetime() throws Exception { assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); assertEquals(50L * 1000L, providerResponse.getExpires().getTime() - providerResponse.getCreated().getTime()); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); } @@ -322,7 +322,7 @@ public void testSaml2FarFutureCreatedLifetime() throws Exception { TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); } @@ -359,7 +359,7 @@ public void testSaml2NoExpires() throws Exception { assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); assertEquals(conditionsProvider.getLifetime() * 1000L, providerResponse.getExpires().getTime() - providerResponse.getCreated().getTime()); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderOnBehalfOfTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderOnBehalfOfTest.java index 9158fc181c8..20a690c33d3 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderOnBehalfOfTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderOnBehalfOfTest.java @@ -82,7 +82,7 @@ public void testDefaultSaml1OnBehalfOfUsernameToken() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -112,7 +112,7 @@ public void testDefaultSaml2OnBehalfOfAssertion() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -149,7 +149,7 @@ public void testCustomHandlingUsernameToken() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("AttributeStatement")); @@ -170,7 +170,7 @@ public void testCustomHandlingUsernameToken() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains("CustomOnBehalfOf")); } @@ -186,7 +186,7 @@ private Element getSAMLAssertion(String user) throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java index 1072e14d0eb..2ef1669d997 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java @@ -72,7 +72,7 @@ public void testRealms() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertTrue(tokenString.contains("A-Issuer")); @@ -86,7 +86,7 @@ public void testRealms() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertFalse(tokenString.contains("A-Issuer")); @@ -100,7 +100,7 @@ public void testRealms() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - token = providerResponse.getToken(); + token = (Element)providerResponse.getToken(); tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(providerResponse.getTokenId())); assertFalse(tokenString.contains("A-Issuer")); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SCTProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SCTProviderTest.java index b0ed2482857..658d24b0ec2 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SCTProviderTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SCTProviderTest.java @@ -63,7 +63,7 @@ public void testCreateSCT() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(ConversationConstants.WSC_NS_05_12)); assertFalse(tokenString.contains(ConversationConstants.WSC_NS_05_02)); @@ -84,7 +84,7 @@ public void testCreateSCTDifferentNamespace() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); String tokenString = DOM2Writer.nodeToString(token); assertTrue(tokenString.contains(ConversationConstants.WSC_NS_05_02)); assertFalse(tokenString.contains(ConversationConstants.WSC_NS_05_12)); @@ -129,7 +129,7 @@ public void testCreateSCTCache() throws Exception { assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - Element token = providerResponse.getToken(); + Element token = (Element)providerResponse.getToken(); SecurityContextToken sctToken = new SecurityContextToken(token); String identifier = sctToken.getIdentifier(); assertNotNull(tokenStore.getToken(identifier)); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerLifetimeTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerLifetimeTest.java index 2a134515208..34a419bf8b4 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerLifetimeTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerLifetimeTest.java @@ -387,7 +387,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerPOPTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerPOPTest.java index 130e9fcb580..00d721175bc 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerPOPTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerPOPTest.java @@ -301,7 +301,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java index 27f487c4095..afee3714168 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java @@ -294,7 +294,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Map getSamlRealms() { diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerTest.java index 646d2ed29d8..3b56bf5e7e1 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerTest.java @@ -594,7 +594,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java index 9e47b643ddb..c12f1c7327b 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java @@ -186,7 +186,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Map getSamlRealms() { diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java index 5c326e871da..08e2551205b 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java @@ -195,7 +195,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Map getSamlRealms() { diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorTest.java index bfb234e3715..b9590976316 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorTest.java @@ -516,7 +516,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Element createSAMLAssertionWithRoles( @@ -546,7 +546,7 @@ private Element createSAMLAssertionWithRoles( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Element createSAMLAssertionWithClaimsProvider( @@ -563,7 +563,7 @@ private Element createSAMLAssertionWithClaimsProvider( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Element createSAMLAssertion( @@ -596,7 +596,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( From b2b0c2af76a5aa77ad5103a3c94afad856e3bbe6 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 14:36:06 +0000 Subject: [PATCH 0013/1346] Adding a system test --- .../cxf/systest/sts/jwt/JWTUnitTest.java | 110 ++++++++++++++++++ .../cxf/systest/sts/deployment/cxf-sts.xml | 3 + .../cxf/systest/sts/jwt/cxf-unit-client.xml | 39 +++++++ 3 files changed, 152 insertions(+) create mode 100644 services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java create mode 100644 services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-unit-client.xml diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java new file mode 100644 index 00000000000..9a17e6cc70a --- /dev/null +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java @@ -0,0 +1,110 @@ +/** + * 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.systest.sts.jwt; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cxf.Bus; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.systest.sts.common.SecurityTestUtil; +import org.apache.cxf.systest.sts.deployment.STSServer; +import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; +import org.apache.cxf.ws.security.SecurityConstants; +import org.apache.cxf.ws.security.tokenstore.SecurityToken; +import org.apache.cxf.ws.security.trust.STSClient; +import org.junit.BeforeClass; + +/** + * Some unit tests to get a JWT token from the STS + */ +public class JWTUnitTest extends AbstractBusClientServerTestBase { + + public static final String JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"; + static final String STSPORT = allocatePort(STSServer.class); + private static final String DEFAULT_ADDRESS = + "https://localhost:8081/doubleit/services/doubleittransportsaml1"; + + @BeforeClass + public static void startServers() throws Exception { + assertTrue( + "Server failed to launch", + // run the server in the same process + // set this to false to fork + launchServer(STSServer.class, true) + ); + } + + @org.junit.AfterClass + public static void cleanup() throws Exception { + SecurityTestUtil.cleanup(); + stopAllServers(); + } + + @org.junit.Test + public void testIssueJWTToken() throws Exception { + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = JWTUnitTest.class.getResource("cxf-unit-client.xml"); + + Bus bus = bf.createBus(busFile.toString()); + SpringBusFactory.setDefaultBus(bus); + SpringBusFactory.setThreadDefaultBus(bus); + + SecurityToken token = + requestSecurityToken(JWT_TOKEN_TYPE, bus, DEFAULT_ADDRESS, null, null); + assertNotNull(token); + assertNotNull(token.getData()); + } + + private SecurityToken requestSecurityToken( + String tokenType, + Bus bus, + String endpointAddress, + Map msgProperties, + String wsdlPort + ) throws Exception { + STSClient stsClient = new STSClient(bus); + String port = STSPORT; + + stsClient.setWsdlLocation("https://localhost:" + port + "/SecurityTokenService/Transport?wsdl"); + stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); + if (wsdlPort != null) { + stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}" + wsdlPort); + } else { + stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"); + } + + Map properties = msgProperties; + if (properties == null) { + properties = new HashMap(); + properties.put(SecurityConstants.USERNAME, "alice"); + properties.put( + SecurityConstants.CALLBACK_HANDLER, + "org.apache.cxf.systest.sts.common.CommonCallbackHandler" + ); + } + + stsClient.setProperties(properties); + stsClient.setTokenType(tokenType); + stsClient.setSendKeyType(false); + + return stsClient.requestSecurityToken(endpointAddress); + } +} diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml index 4a9c1183bbf..989873f5d93 100644 --- a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml @@ -50,6 +50,7 @@ + @@ -62,6 +63,8 @@ + + + + + + + + + + + + + + + + + + + + + + From ff693a62de7674464fe28265c48ca07dcdb64026 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 14:36:19 +0000 Subject: [PATCH 0014/1346] Fix a problem with returning a token --- .../cxf/ws/security/trust/AbstractSTSClient.java | 9 ++++++++- .../cxf/sts/operation/TokenIssueOperation.java | 16 ++++++++++++++-- .../cxf/sts/operation/IssueJWTUnitTest.java | 10 ++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java index 0784b617990..f06ff8072a2 100755 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java @@ -1423,7 +1423,8 @@ protected SecurityToken createSecurityToken(Element el, byte[] requestorEntropy) Element entropy = null; String tt = null; String retKeySize = null; - + String tokenData = null; + while (el != null) { String ln = el.getLocalName(); if (namespace.equals(el.getNamespaceURI())) { @@ -1431,6 +1432,9 @@ protected SecurityToken createSecurityToken(Element el, byte[] requestorEntropy) lte = el; } else if ("RequestedSecurityToken".equals(ln)) { rst = DOMUtils.getFirstElement(el); + if (rst == null) { + tokenData = el.getTextContent(); + } } else if ("RequestedAttachedReference".equals(ln)) { rar = DOMUtils.getFirstElement(el); } else if ("RequestedUnattachedReference".equals(ln)) { @@ -1457,6 +1461,9 @@ protected SecurityToken createSecurityToken(Element el, byte[] requestorEntropy) token.setUnattachedReference(rur); token.setIssuerAddress(location); token.setTokenType(tt); + if (tokenData != null) { + token.setData(tokenData.getBytes()); + } byte[] secret = null; diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java index 1d0c378200c..39f5b6b50e1 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java @@ -31,12 +31,15 @@ import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; +import org.w3c.dom.Document; import org.w3c.dom.Element; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.rt.security.claims.ClaimCollection; import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; import org.apache.cxf.sts.event.STSIssueFailureEvent; import org.apache.cxf.sts.event.STSIssueSuccessEvent; import org.apache.cxf.sts.request.KeyRequirements; @@ -286,7 +289,16 @@ private RequestSecurityTokenResponseType createResponse( QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(requestedTokenType); LOG.fine("Encrypting Issued Token: " + encryptIssuedToken); if (!encryptIssuedToken) { - requestedTokenType.setAny(tokenResponse.getToken()); + if (tokenResponse.getToken() instanceof String) { + Document doc = DOMUtils.newDocument(); + Element requestedTokenEl = doc.createElementNS(STSConstants.WST_NS_05_12, + "RequestedSecurityToken"); + requestedTokenEl.setTextContent((String)tokenResponse.getToken()); + response.getAny().add(requestedTokenEl); + } else { + requestedTokenType.setAny(tokenResponse.getToken()); + response.getAny().add(requestedToken); + } } else { if (!(tokenResponse.getToken() instanceof Element)) { throw new STSException("Error in creating the response", STSException.REQUEST_FAILED); @@ -297,8 +309,8 @@ private RequestSecurityTokenResponseType createResponse( encryptionProperties, keyRequirements, webServiceContext ) ); + response.getAny().add(requestedToken); } - response.getAny().add(requestedToken); if (returnReferences) { // RequestedAttachedReference diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java index 6112d2f81af..58d6b25114a 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java @@ -51,7 +51,6 @@ import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; -import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.cxf.ws.security.tokenstore.TokenStore; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; @@ -130,11 +129,10 @@ public void testIssueJWTToken() throws Exception { // Test the generated token. String jwtToken = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof JAXBElement - && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { - RequestedSecurityTokenType rstType = - (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); - jwtToken = (String)rstType.getAny(); + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); break; } } From b79272c09949cea80572fb86bc170cb344a9b731 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 14:58:30 +0000 Subject: [PATCH 0015/1346] Fixing some tests --- osgi/karaf/features/src/main/resources/features.xml | 2 ++ .../test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java | 2 +- .../apache/cxf/systest/sts/delegation/SAMLDelegationTest.java | 4 ++-- .../org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osgi/karaf/features/src/main/resources/features.xml b/osgi/karaf/features/src/main/resources/features.xml index 80c339c0df5..7c1681ee59c 100644 --- a/osgi/karaf/features/src/main/resources/features.xml +++ b/osgi/karaf/features/src/main/resources/features.xml @@ -402,6 +402,8 @@ mvn:com.hazelcast/hazelcast/${cxf.hazelcast.version} mvn:net.sf.ehcache/ehcache/${cxf.ehcache.version} + mvn:org.apache.cxf/cxf-rt-rs-json-basic/${project.version} + mvn:org.apache.cxf/cxf-rt-rs-security-jose/${project.version} cxf-core cxf-ws-security mvn:org.apache.cxf.services.sts/cxf-services-sts-core/${project.version} diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java index 9a17e6cc70a..837db4317ca 100644 --- a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java @@ -98,7 +98,7 @@ private SecurityToken requestSecurityToken( properties.put( SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler" - ); + ); } stsClient.setProperties(properties); diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/delegation/SAMLDelegationTest.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/delegation/SAMLDelegationTest.java index 4126a443510..502fb5572f2 100644 --- a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/delegation/SAMLDelegationTest.java +++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/delegation/SAMLDelegationTest.java @@ -326,7 +326,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private Element createUnsignedSAMLAssertion( @@ -344,7 +344,7 @@ private Element createUnsignedSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java index 10463363d6f..d3ea7385638 100644 --- a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java +++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java @@ -571,7 +571,7 @@ private Element createSAMLAssertion( assertTrue(providerResponse != null); assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); - return providerResponse.getToken(); + return (Element)providerResponse.getToken(); } private TokenProviderParameters createProviderParameters( From b43822b3fa1224036c8a03ae4a11593a8184645e Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 6 Nov 2015 15:50:38 +0000 Subject: [PATCH 0016/1346] [CXF-6668] Updating the test --- .../wadlto/jaxrs/JAXRSContainerTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java index e4ad62dd040..5c0ab3401da 100644 --- a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java +++ b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java @@ -20,10 +20,19 @@ package org.apache.cxf.tools.wadlto.jaxrs; import java.io.File; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.PUT; +import javax.ws.rs.QueryParam; + import org.apache.cxf.helpers.FileUtils; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.apache.cxf.tools.common.ProcessorTestBase; import org.apache.cxf.tools.common.ToolContext; import org.apache.cxf.tools.wadlto.WadlToolConstants; @@ -333,6 +342,63 @@ public void testQueryMultipartParam() { assertEquals(2, files.size()); assertTrue(checkContains(files, "application.Test1.class")); assertTrue(checkContains(files, "application.Test2.class")); + + @SuppressWarnings("resource") + ClassLoader loader = new URLClassLoader(new URL[] {output.toURI().toURL() }); + + Class test1 = loader.loadClass("application.Test1"); + Method[] test1Methods = test1.getDeclaredMethods(); + assertEquals(1, test1Methods.length); + + assertEquals(2, test1Methods[0].getAnnotations().length); + assertNotNull(test1Methods[0].getAnnotation(PUT.class)); + Consumes consumes1 = test1Methods[0].getAnnotation(Consumes.class); + assertNotNull(consumes1); + assertEquals(1, consumes1.value().length); + assertEquals("multipart/mixed", consumes1.value()[0]); + + assertEquals("put", test1Methods[0].getName()); + Class[] paramTypes = test1Methods[0].getParameterTypes(); + assertEquals(3, paramTypes.length); + Annotation[][] paramAnns = test1Methods[0].getParameterAnnotations(); + assertEquals(Boolean.class, paramTypes[0]); + assertEquals(1, paramAnns[0].length); + QueryParam test1QueryParam1 = (QueryParam)paramAnns[0][0]; + assertEquals("standalone", test1QueryParam1.value()); + assertEquals(String.class, paramTypes[1]); + assertEquals(1, paramAnns[1].length); + Multipart test1MultipartParam1 = (Multipart)paramAnns[1][0]; + assertEquals("action", test1MultipartParam1.value()); + assertTrue(test1MultipartParam1.required()); + assertEquals(String.class, paramTypes[2]); + assertEquals(1, paramAnns[2].length); + Multipart test1MultipartParam2 = (Multipart)paramAnns[2][0]; + assertEquals("sources", test1MultipartParam2.value()); + assertFalse(test1MultipartParam2.required()); + + Class test2 = loader.loadClass("application.Test2"); + Method[] test2Methods = test2.getDeclaredMethods(); + assertEquals(1, test2Methods.length); + + assertEquals(2, test2Methods[0].getAnnotations().length); + assertNotNull(test2Methods[0].getAnnotation(PUT.class)); + Consumes consumes2 = test2Methods[0].getAnnotation(Consumes.class); + assertNotNull(consumes2); + assertEquals(1, consumes2.value().length); + assertEquals("application/json", consumes2.value()[0]); + + assertEquals("put", test2Methods[0].getName()); + Class[] paramTypes2 = test2Methods[0].getParameterTypes(); + assertEquals(2, paramTypes2.length); + Annotation[][] paramAnns2 = test2Methods[0].getParameterAnnotations(); + assertEquals(boolean.class, paramTypes2[0]); + assertEquals(1, paramAnns2[0].length); + QueryParam test2QueryParam1 = (QueryParam)paramAnns2[0][0]; + assertEquals("snapshot", test2QueryParam1.value()); + assertEquals(String.class, paramTypes2[1]); + assertEquals(0, paramAnns2[1].length); + + } catch (Exception e) { e.printStackTrace(); fail(); From 47f3416a37c6d250ed4325caee2caa81623ee851 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 16:04:17 +0000 Subject: [PATCH 0017/1346] Fixing karaf feature for jose --- osgi/karaf/features/src/main/resources/features.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osgi/karaf/features/src/main/resources/features.xml b/osgi/karaf/features/src/main/resources/features.xml index 7c1681ee59c..da1f799ffa4 100644 --- a/osgi/karaf/features/src/main/resources/features.xml +++ b/osgi/karaf/features/src/main/resources/features.xml @@ -220,7 +220,9 @@ cxf-jaxrs cxf-rt-security + mvn:org.apache.cxf/cxf-rt-rs-json-basic/${project.version} mvn:org.apache.cxf/cxf-rt-rs-security-jose/${project.version} + mvn:org.apache.cxf/cxf-rt-rs-security-jose-jaxrs/${project.version} cxf-rs-security-jose From 22766066de126ec09e56702f04bfe8c7be423576 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 6 Nov 2015 16:54:49 +0000 Subject: [PATCH 0018/1346] [CXF-6670] Fixing a class cast issue --- .../cxf/jaxrs/utils/InjectionUtils.java | 10 ++++- .../apache/cxf/systest/jaxrs/BookServer.java | 39 +++++++++++++------ .../apache/cxf/systest/jaxrs/BookStore.java | 5 ++- .../jaxrs/JAXRSClientServerBookTest.java | 5 ++- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java index 3850393b659..fa42c6f02af 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/InjectionUtils.java @@ -357,6 +357,7 @@ public static Object extractFromMethod(Object requestObject, return null; } + @SuppressWarnings("unchecked") public static T handleParameter(String value, boolean decoded, Class pClass, @@ -385,11 +386,16 @@ public static T handleParameter(String value, throw createParamConversionException(pType, nfe); } if (result != null) { - return pClass.cast(result); + T theResult = null; + if (pClass.isPrimitive()) { + theResult = (T)result; + } else { + theResult = pClass.cast(result); + } + return theResult; } if (pClass.isPrimitive()) { try { - @SuppressWarnings("unchecked") T ret = (T)PrimitiveUtils.read(value, pClass); // cannot us pClass.cast as the pClass is something like // Boolean.TYPE (representing the boolean primitive) and diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer.java index 8c7f992a438..9c1764de2d2 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer.java @@ -124,7 +124,7 @@ protected void run() { providers.add(new BlockingRequestFilter()); providers.add(new FaultyResponseFilter()); providers.add(new BlockedExceptionMapper()); - providers.add(new XmlBookParamConverter()); + providers.add(new ParamConverterImpl()); sf.setProviders(providers); List> inInts = new ArrayList>(); inInts.add(new CustomInFaultyInterceptor()); @@ -270,7 +270,7 @@ public void filter(ClientRequestContext requestContext, ClientResponseContext re } } - public static class XmlBookParamConverter implements ParamConverterProvider { + public static class ParamConverterImpl implements ParamConverterProvider { @Context private Providers providers; @@ -278,20 +278,35 @@ public static class XmlBookParamConverter implements ParamConverterProvider { @Override public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { - if (rawType != Book.class) { + if (rawType == Book.class) { + + MessageBodyReader mbr = providers.getMessageBodyReader(Book.class, + Book.class, + annotations, + MediaType.APPLICATION_XML_TYPE); + MessageBodyWriter mbw = providers.getMessageBodyWriter(Book.class, + Book.class, + annotations, + MediaType.APPLICATION_XML_TYPE); + return (ParamConverter)new XmlParamConverter(mbr, mbw); + } else if (rawType == byte.class) { + return (ParamConverter)new ByteConverter(); + } else { return null; } - MessageBodyReader mbr = providers.getMessageBodyReader(Book.class, - Book.class, - annotations, - MediaType.APPLICATION_XML_TYPE); - MessageBodyWriter mbw = providers.getMessageBodyWriter(Book.class, - Book.class, - annotations, - MediaType.APPLICATION_XML_TYPE); - return (ParamConverter)new XmlParamConverter(mbr, mbw); } + private static class ByteConverter implements ParamConverter { + @Override + public Byte fromString(String t) { + return new Byte(t); + } + + @Override + public String toString(Byte b) { + return b.toString(); + } + } private static class XmlParamConverter implements ParamConverter { private MessageBodyReader mbr; private MessageBodyWriter mbw; diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java index 48377846030..cde5c7f91ca 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java @@ -306,7 +306,10 @@ public Book deleteBodyBook(Book book) { @POST @Path("/echoxmlbookquery") @Produces("application/xml") - public Book echoXmlBookQuery(@QueryParam("book") Book book) { + public Book echoXmlBookQuery(@QueryParam("book") Book book, @QueryParam("id") byte id) { + if (book.getId() != (long)id) { + throw new RuntimeException(); + } return book; } diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java index 2bb461b2db2..33ea4158b82 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java @@ -135,11 +135,12 @@ public void testUpdateBookWithProxy() throws Exception { public void testEchoXmlBookQuery() throws Exception { String address = "http://localhost:" + PORT; BookStore store = JAXRSClientFactory.create(address, BookStore.class, - Collections.singletonList(new BookServer.XmlBookParamConverter())); - Book b = store.echoXmlBookQuery(new Book("query", 125L)); + Collections.singletonList(new BookServer.ParamConverterImpl())); + Book b = store.echoXmlBookQuery(new Book("query", 125L), (byte)125); assertEquals(125L, b.getId()); assertEquals("query", b.getName()); } + @Test public void testGetBookRoot() throws Exception { String address = "http://localhost:" + PORT + "/bookstore/;JSESSIONID=xxx"; From c129ebc7306e29c6010cc544ce4b8e723dbbd504 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 17:21:29 +0000 Subject: [PATCH 0019/1346] Adding the ability to sign JWT tokens in the STS plus some NPE fixes --- .../org/apache/cxf/message/MessageUtils.java | 8 +- .../security/jose/common/JoseConstants.java | 5 + .../jose/common/KeyManagementUtils.java | 37 +++--- .../cxf/rs/security/jose/jws/JwsUtils.java | 2 +- .../token/provider/jwt/JWTTokenProvider.java | 115 +++++++++++++++--- .../token/provider/JWTTokenProviderTest.java | 40 ++++++ 6 files changed, 168 insertions(+), 39 deletions(-) diff --git a/core/src/main/java/org/apache/cxf/message/MessageUtils.java b/core/src/main/java/org/apache/cxf/message/MessageUtils.java index d8bbf0c4e4d..d964070131c 100644 --- a/core/src/main/java/org/apache/cxf/message/MessageUtils.java +++ b/core/src/main/java/org/apache/cxf/message/MessageUtils.java @@ -124,9 +124,11 @@ public static boolean isTrue(Object value) { } public static boolean getContextualBoolean(Message m, String key, boolean defaultValue) { - Object o = m.getContextualProperty(key); - if (o != null) { - return PropertyUtils.isTrue(o); + if (m != null) { + Object o = m.getContextualProperty(key); + if (o != null) { + return PropertyUtils.isTrue(o); + } } return defaultValue; } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java index cc990b5c600..cf9f90a069c 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java @@ -94,6 +94,11 @@ public final class JoseConstants { */ public static final String RSSEC_KEY_STORE_FILE = "rs.security.keystore.file"; + /** + * The KeyStore Object. + */ + public static final String RSSEC_KEY_STORE= "rs.security.keystore"; + /** * A reference to a PrivateKeyPasswordProvider instance used to retrieve passwords to access keys. */ diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java index a53e7a8c574..2ca6e805849 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -139,8 +139,6 @@ private static String getMessageProperty(Message m, String keyStoreLocPropPrefer private static PrivateKey loadPrivateKey(KeyStore keyStore, Message m, Properties props, - Bus bus, - PrivateKeyPasswordProvider provider, KeyOperation keyOper, String alias) { @@ -149,8 +147,11 @@ private static PrivateKey loadPrivateKey(KeyStore keyStore, if (theAlias != null) { props.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, theAlias); } - char[] keyPswdChars = provider != null ? provider.getPassword(props) - : keyPswd != null ? keyPswd.toCharArray() : null; + char[] keyPswdChars = keyPswd != null ? keyPswd.toCharArray() : null; + if (keyPswdChars == null) { + PrivateKeyPasswordProvider provider = loadPasswordProvider(m, props, keyOper); + keyPswdChars = provider != null ? provider.getPassword(props) : null; + } return CryptoUtils.loadPrivateKey(keyStore, keyPswdChars, theAlias); } @@ -174,7 +175,7 @@ public static String getKeyId(Message m, Properties props, KeyOperation keyOper) { String kid = null; String altPropertyName = null; - if (keyOper != null) { + if (keyOper != null && m != null) { if (keyOper == KeyOperation.ENCRYPT || keyOper == KeyOperation.DECRYPT) { altPropertyName = preferredPropertyName + ".jwe"; } else if (keyOper == KeyOperation.SIGN || keyOper == KeyOperation.VERIFY) { @@ -216,21 +217,25 @@ public static PrivateKey loadPrivateKey(Message m, Properties props, KeyOperatio KeyStore keyStore = loadPersistKeyStore(m, props); return loadPrivateKey(keyStore, m, props, keyOper, null); } - private static PrivateKey loadPrivateKey(KeyStore keyStore, Message m, Properties props, KeyOperation keyOper, - String alias) { - Bus bus = m.getExchange().getBus(); - PrivateKeyPasswordProvider cb = loadPasswordProvider(m, props, keyOper); - return loadPrivateKey(keyStore, m, props, bus, cb, keyOper, alias); - } public static KeyStore loadPersistKeyStore(Message m, Properties props) { - if (!props.containsKey(JoseConstants.RSSEC_KEY_STORE_FILE)) { - LOG.warning("No keystore file has been configured"); - throw new JoseException("No keystore file has been configured"); + KeyStore keyStore = null; + if (props.containsKey(JoseConstants.RSSEC_KEY_STORE)) { + keyStore = (KeyStore)props.get(JoseConstants.RSSEC_KEY_STORE); + } + + if (keyStore == null) { + if (!props.containsKey(JoseConstants.RSSEC_KEY_STORE_FILE)) { + LOG.warning("No keystore file has been configured"); + throw new JoseException("No keystore file has been configured"); + } + keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE)); } - KeyStore keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE)); + if (keyStore == null) { keyStore = loadKeyStore(props, m.getExchange().getBus()); - m.getExchange().put((String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE), keyStore); + if (m != null) { + m.getExchange().put((String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE), keyStore); + } } return keyStore; } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index b9f0001a9ef..0bce50e39eb 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -261,7 +261,7 @@ public static boolean validateCriticalHeaders(JoseHeaders headers) { //TODO: validate JWS specific constraints return JoseUtils.validateCriticalHeaders(headers); } - private static JwsSignatureProvider loadSignatureProvider(Message m, + public static JwsSignatureProvider loadSignatureProvider(Message m, Properties props, JoseHeaders headers, boolean ignoreNullProvider) { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java index b4582811bbe..573788bbd25 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -19,6 +19,7 @@ package org.apache.cxf.sts.token.provider.jwt; +import java.security.KeyStore; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -26,17 +27,29 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; + import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; +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.jose.jwt.JwtToken; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.SignatureProperties; import org.apache.cxf.sts.request.TokenRequirements; import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; import org.apache.cxf.sts.token.realm.SAMLRealm; import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.Merlin; +import org.apache.wss4j.common.ext.WSPasswordCallback; /** * A TokenProvider implementation that provides a JWT Token. @@ -85,13 +98,6 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters JwtClaims claims = jwtClaimsProvider.getJwtClaims(jwtClaimsProviderParameters); - /* - if (signToken) { - STSPropertiesMBean stsProperties = tokenParameters.getStsProperties(); - signToken(assertion, samlRealm, stsProperties, tokenParameters.getKeyRequirements()); - } - */ - try { /* Document doc = DOMUtils.createDocument(); @@ -120,12 +126,8 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters JwtToken token = new JwtToken(claims); - Properties signingProperties = new Properties(); - signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, "none"); - - JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token); - jws.setSignatureProperties(signingProperties); - String tokenData = jws.getSignedEncodedJws(); + String tokenData = signToken(token, null, tokenParameters.getStsProperties(), + tokenParameters.getTokenRequirements()); TokenProviderResponse response = new TokenProviderResponse(); response.setToken(tokenData); @@ -139,12 +141,6 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters response.setExpires(new Date(claims.getExpiryTime() * 1000L)); } - /*response.setEntropy(entropyBytes); - if (keySize > 0) { - response.setKeySize(keySize); - } - response.setComputedKey(computedKey); - */ LOG.fine("JWT Token successfully created"); return response; } catch (Exception e) { @@ -192,4 +188,85 @@ public void setJwtClaimsProvider(JWTClaimsProvider jwtClaimsProvider) { this.jwtClaimsProvider = jwtClaimsProvider; } + private String signToken( + JwtToken token, + SAMLRealm samlRealm, + STSPropertiesMBean stsProperties, + TokenRequirements tokenRequirements + ) throws Exception { + + Properties signingProperties = new Properties(); + + if (signToken) { + // Initialise signature objects with defaults of STSPropertiesMBean + Crypto signatureCrypto = stsProperties.getSignatureCrypto(); + CallbackHandler callbackHandler = stsProperties.getCallbackHandler(); + SignatureProperties signatureProperties = stsProperties.getSignatureProperties(); + String alias = stsProperties.getSignatureUsername(); + + if (samlRealm != null) { + // If SignatureCrypto configured in realm then + // callbackhandler and alias of STSPropertiesMBean is ignored + if (samlRealm.getSignatureCrypto() != null) { + LOG.fine("SAMLRealm signature keystore used"); + signatureCrypto = samlRealm.getSignatureCrypto(); + callbackHandler = samlRealm.getCallbackHandler(); + alias = samlRealm.getSignatureAlias(); + } + // SignatureProperties can be defined independently of SignatureCrypto + if (samlRealm.getSignatureProperties() != null) { + signatureProperties = samlRealm.getSignatureProperties(); + } + } + + // Get the signature algorithm to use - for now we don't allow the client to ask + // for a particular signature algorithm, as with SAML + String signatureAlgorithm = signatureProperties.getSignatureAlgorithm(); + try { + SignatureAlgorithm.getAlgorithm(signatureAlgorithm); + } catch (IllegalArgumentException ex) { + signatureAlgorithm = SignatureAlgorithm.RS256.name(); + } + + // If alias not defined, get the default of the SignatureCrypto + if ((alias == null || "".equals(alias)) && (signatureCrypto != null)) { + alias = signatureCrypto.getDefaultX509Identifier(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Signature alias is null so using default alias: " + alias); + } + } + // Get the password + WSPasswordCallback[] cb = {new WSPasswordCallback(alias, WSPasswordCallback.SIGNATURE)}; + callbackHandler.handle(cb); + String password = cb[0].getPassword(); + + signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, signatureAlgorithm); + signingProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias); + signingProperties.put(JoseConstants.RSSEC_KEY_PSWD, password); + + if (!(signatureCrypto instanceof Merlin)) { + throw new STSException("Can't get the keystore", STSException.REQUEST_FAILED); + } + KeyStore keystore = ((Merlin)signatureCrypto).getKeyStore(); + signingProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); + + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token); + jws.setSignatureProperties(signingProperties); + + Message m = PhaseInterceptorChain.getCurrentMessage(); + JwsSignatureProvider sigProvider = + JwsUtils.loadSignatureProvider(m, signingProperties, token.getJwsHeaders(), false); + token.getJwsHeaders().setSignatureAlgorithm(sigProvider.getAlgorithm()); + + return jws.signWith(sigProvider); + } else { + signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, "none"); + + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token); + jws.setSignatureProperties(signingProperties); + return jws.getSignedEncodedJws(); + } + + } + } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java index 19d41f218a4..aed28ef0966 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java @@ -18,11 +18,13 @@ */ package org.apache.cxf.sts.token.provider; +import java.security.cert.X509Certificate; import java.util.Properties; import org.apache.cxf.jaxws.context.WebServiceContextImpl; import org.apache.cxf.jaxws.context.WrappedMessageContext; import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.rs.security.jose.jwt.JwtToken; @@ -36,6 +38,7 @@ import org.apache.cxf.ws.security.tokenstore.TokenStore; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.crypto.CryptoType; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.common.principal.CustomTokenPrincipal; import org.junit.Assert; @@ -50,6 +53,7 @@ public class JWTTokenProviderTest extends org.junit.Assert { @org.junit.Test public void testCreateUnsignedJWT() throws Exception { TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(false); TokenProviderParameters providerParameters = createProviderParameters(); @@ -73,6 +77,42 @@ public void testCreateUnsignedJWT() throws Exception { jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); } + @org.junit.Test + public void testCreateSignedJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + + // Verify Signature + Crypto crypto = providerParameters.getStsProperties().getSignatureCrypto(); + CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); + cryptoType.setAlias(providerParameters.getStsProperties().getSignatureUsername()); + X509Certificate[] certs = crypto.getX509Certificates(cryptoType); + assertNotNull(certs); + + assertTrue(jwtConsumer.verifySignatureWith(certs[0], SignatureAlgorithm.RS256)); + } + private TokenProviderParameters createProviderParameters() throws WSSecurityException { TokenProviderParameters parameters = new TokenProviderParameters(); From 9245fcb3947411433c3c03ca7aff967365ec3fdd Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 6 Nov 2015 18:02:04 +0000 Subject: [PATCH 0020/1346] Updating STS keys to be 2048 bits --- .../src/main/webapp/WEB-INF/servicestore.jks | Bin 3350 -> 3475 bytes .../src/main/webapp/WEB-INF/servicestore.jks | Bin 3350 -> 3475 bytes .../release/samples/sts/keys/clientstore.jks | Bin 4436 -> 4559 bytes .../release/samples/sts/keys/servicestore.jks | Bin 3350 -> 3475 bytes .../release/samples/sts/keys/stsstore.jks | Bin 3978 -> 4557 bytes .../security/jose/common/JoseConstants.java | 2 +- .../src/test/resources/clientstore.jks | Bin 0 -> 4559 bytes .../src/test/resources/servicestore.jks | Bin 3350 -> 3475 bytes .../sts-core/src/test/resources/stsstore.jks | Bin 3978 -> 4557 bytes .../sts-war/src/main/resources/stsstore.jks | Bin 3978 -> 4557 bytes .../src/test/resources/clientstore.jks | Bin 4436 -> 4559 bytes .../systest/sts/realms/stsstore_realms.jks | Bin 5394 -> 4557 bytes .../src/test/resources/servicestore.jks | Bin 3350 -> 3475 bytes .../advanced/src/test/resources/stsstore.jks | Bin 3978 -> 4557 bytes .../basic/src/test/resources/clientstore.jks | Bin 4436 -> 4559 bytes .../basic/src/test/resources/servicestore.jks | Bin 3350 -> 3475 bytes .../basic/src/test/resources/stsstore.jks | Bin 3978 -> 4557 bytes .../src/test/resources/clientstore.jks | Bin 4436 -> 4559 bytes .../sts-osgi/src/main/resources/stsstore.jks | Bin 3978 -> 4557 bytes .../src/test/resources/clientstore.jks | Bin 4436 -> 4559 bytes .../src/test/resources/servicestore.jks | Bin 3350 -> 3475 bytes .../kerberos/src/test/resources/stsstore.jks | Bin 3978 -> 4557 bytes 22 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 services/sts/sts-core/src/test/resources/clientstore.jks diff --git a/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/servicestore.jks b/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/servicestore.jks index 0dfa20605099f1c5383579233891ef083525c018..999ee824c3248c0b89da0336bdc451678795fbc2 100644 GIT binary patch delta 2771 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 literal 3350 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 diff --git a/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/servicestore.jks b/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/servicestore.jks index 0dfa20605099f1c5383579233891ef083525c018..999ee824c3248c0b89da0336bdc451678795fbc2 100644 GIT binary patch delta 2771 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 literal 3350 zcmds(XHZky7RPgvP(u$*Y@sMp#1qg+FT$mW0#a1KhaiR`gh1#`NI)ba0$%W<2!tjm zEubhMNE1Pnat&1}4{6dwM2ZsdC5R5=`0ktc@!k)6_N=wn*>l#c{a?SeKD#~(fk2>) z1>4x!ae>}G-fq}H2n0|-G-J0zSWYV(0#z92Qy>X8&RC`t7!-g)IUq|JRy2&+N(#u$ z${`zN7`4m|04yvJka!o306AD;R&Z7%)XDRIVp8kw4)g+W1P zC<4iCWQo$ldO8q%a99r?v>+(J^uv${e}8`kMj{H1cpO>`6#emo9Dp!_;n&_g19ypV zqtT!usDf5jQbsG;h;S=1P0XhGx5|Z>zO@nn3j{`tWmJOLSF&3Z zuSt-PY_IA{<{9-Azodg0Q#0P%D0J?0s@JN_ThkQnNPRO=!Hf<46Jch8_r(;k1$oy!6e6%2yMhZA}Phb`>RY0}L;LoWCFmt?T^^g#i#Civ$ROB!CCp$*9s!9=I@E(58c%9z`}2FFhvYF+P?^{jx`Q z2S^-bo=d`s-5`+`{>zvQVsSRH;O|r((iXvRG4n&hSwI*7eBD|!hJ0X*YRQH+7aA_r zJjoTepvm^T{>!OLE@f)!`R?HoZK{ol&y35*_icp~iF$)v4i!6gU?qHTy7!9A`0B82 zh|5GpWxcQ^oD$O`l_$S5R5kf=X4I!`c5NAbKF6FLG9vG@F2b>WVN-sOplhScRQTesR%eK}o0P@K9UyU=0UcD`DXBju?!ma^QvO ztNSqP$66@2`m4hVoRO&matXmnhE4&9NW3m@S_w(u8D(IBd~5H-bH$gKh&$_X0hN|) zxWwW7-lKSIgot}5z0kiR?oj7d;KXbEv7*88Q9ZXIQsxAtxU^dT>QcNiNr+|!*mJ&m z)Q~f1Ft#lk|`o z-kCT_RMD>H6pfTMBb2rx;krqJ!Gz;nOMswMZHiD*!l*ozl~@&Q)*y9eES!2MQB>qG z-$J&+=<=dt=?6(rDBddFKeRf4+I8N}{G+0aZ>LVHKS%$}u| zaTWbR?tGB*Fh;SYJtMA7w$1sEFZqk!nR~l~RH|Qi4FzWOh+&6~jpZ~JF`0gabfd7z zcjGnGqctBUAKc)l-t8HyA)Vpo!9HyscGp^)kjt$K@+TKI*V4US_O*)LE-_3gxi(%g z;^i!86eHE!N2$#cl*nn%^o|8!!j$7#73$$Dx({7fIP2Z%FxhQl8q+oQuFtJz15U;W z?azYvRkDo>CW^RCMlp z4@n1FzV_F-obH=6xSm#KU7!`}ZLNIMC8*YEu~p2+wZP1%anJR0@;YDdSpPfdG5K9O z-)zB&_y&KWx@R+|O*%GS_IISg)j$&bFav3n??|g887BL#Us?D`lOXZwe?yuH)*oe! zC%B>c7?RjP8ru~nawX40kGh?>W?w|^Y*+RPw^PiIDUxDzYVx{i8)FR1)EO6go zn=n7q2Wht>)Zc5cx{~!I$#J)G?pQn0^fdVHNgy7!W)r!Ak?5J&+=L}AY+p^;Lh8F$ zQ;`doG^6V1*${bK`??BRa>eLn%W}mU#8P~dqvF1Mc+H;vHvJi6z`tnvQP67VZ=a;& z++%Uj$jk}H14nV+LAaj*LYXZf#Fw&63q(&DX6{>!dJ^?}{HI8NDoFgy1O)?x^C0nk zxZtm1GKekN#HN2p8nP;o8l`8*}mz(Q+50ldU65 z+IfWRbrGSg?Q8P4?$5PtYf+H&o6n2C({A=>XYdyrZtK*z;y%w;8S6APZMn#eaT$RN z^ugmC9elna60M>4=IxJ(8OUPPIGwTBzqJ?m@%ae!&z(i^`z#DYa=7Ci9o${;-aeZ` z#h^h@Q3<28IhJC;!<#1f>#zRm-M`82&CFm;M=BYkce!;KR@7As-|H-!yx36{^14-L ze(qeS16^GvsAz0ZKaZ@aqDzayNu*-E?rGdtZOT?H9WcNbMQeB)wNbeKT)-KkO*_Nh zs1{3|v`bXCHsvq(3OSR`KJcEOGLRB%sZ~>W-Ifhir#zt6jD-Ada-b21YT9`BKPAr` zb@aQ%itF#HKAAWAtrK|LR diff --git a/distribution/src/main/release/samples/sts/keys/clientstore.jks b/distribution/src/main/release/samples/sts/keys/clientstore.jks index 23168a909516c0ee220c11b2388ba042be769ee6..f734f87b3a820f9fb99169eaf1ce838603b644a7 100644 GIT binary patch delta 3488 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 literal 3350 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 diff --git a/distribution/src/main/release/samples/sts/keys/stsstore.jks b/distribution/src/main/release/samples/sts/keys/stsstore.jks index e805906aadf5055a49caaf72093a8a5312dc7172..51f131f22330e982873f02fbf58b7b946b1444bc 100644 GIT binary patch delta 3471 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 zcmdtkc{J2(9{})~F}4{qVJzu{LD`qz*iFcmv{)f~ZcJ80uKb|w^`#jJ2o#%VL&-e4p!iR+qFc=I0 z4K8TFVSMyZ{{Vk4S|}8#qbtjwL5nOP3Q+-Ic~B}ma3owAAq0nW!;n!3=2s7oD5ZsK zj?Mgu`PFg3nG(}&r-~j{X*A4UB+jK-9KRj97Cg(csuMKPwRWve$<%Jxm1&kc;B0fe zzoSd$4$+HBUO&SY%rJ<0$HYygWtrmodP%$dH>(FdaUPqUdQx9vgsEoQHNJi54NH`k zI5uUW8WdeQUp8OXPJF#{WJG&Qms-b%{+GL0T)9QGDl#CasMp=2DejBVe>S!4)Z?_b1$HdOnjhSK`Q#6c>chyZq!pY z=eetyTqC>p!m-;)vDS6hxCo%KerNXhjJJfg!@6#FmRoDGV}1FVZb}NnV9yIebi&o? z8E`EaP(1LQyd$FM+wjG75yy-Jc_0SFoYZ_Ix~501Z9`S}*_5EzZw0BlB!)Y)cOENI zE#Xa|w~C}*U;m$h6k6JlT{*t<8ylM|Cy)~@)Z#*)*`rzuFJbrVx)+$07aZ1^H|`ty z`X1%KujPyK&7Idj5Baq}b~bj}8jniV7fY&3O{${h$ewDI&({;U(-XoE8dCOAd#4U= z_cBV?z0{^c{FK6YdAa`H9r(8M$9G{O^&_FP*RsSo{5e{BSWeG;gxJ21tox2n0YAFd*n_=@Yb^#4(zjPSC;szw3=1YtQt^HB|AjJ9Kea;?U`#thY%#@nybu>*cGv8L#En|8?8 ziSFN%d2mkYt?e20$xAA5z6p!-k=pCz&2AQk+teGg?w`v)L#&Aw4)c*omL4P6Ua0z- z_b~^4T77HSVBe`Wkv@{ke&cz&uZZj(UA3@>8zx-w?T_`$dtRInCDycmMR37k@SG^P zFc1aD0+LXbB(X>_q=?fC%zRveolMzIQJ=wgr?1U!G+z&Z-JEqfB-jdo&5=Lc!vsrm z!J=QOJW;C}Mu3wa4#^F0!Qp?bE*jK6Tu`-?BOVu9O;z8`lOZ)LcX~Rzy;3*1iAsZWeS5<_hFG`mi;v!rDBivVBB0NiLE#9?80@C)$CH+hgSTV&26?*D82?a*wSXAs zn_Rdxi#jZQY*7eV0KZxY+;ZL~%fFmGtEJF30CZPLu^9ea()>F_o@w4Pf1i0(PMaGU z80K^&$hQ2#`Dnv&BOcEL3%P`o8J8|QQkyM|1Pf%*H=gH$7WazC z_e~=)ld;BeHS@VJ)xE#hu$t3Z14qd>HLKB6$&aX-iUq#LZJiC4V>{r%S4S%k&9!X# zqHxmt1RW8RJw(;mPG3Pp4MId|6(YW6+@r$rBUagpb8&a$Qj)*K1YZNdubd`>5SakL z@ko&$?O}rTx!|LJN#!74zIWiyd_l-GF^^t0kp>vzO338jsjhAJfIo-&vO+8;Y^P9u;WYSr7>POY+c$EVR>5eg_ zjabp&&CwA&jo(x=u77Nhs8^Q@n#fPS+-Uc*C2ZCSg@?V+bg`(OGW<4%Q}b`F^HEVE4-wfa*RkTE#dkfURXcnW%=&{FAihlNpYtzD0oYX zx3@n*6;NJ6Ms67y^TqT0LsrBA(Ir-}99EW6T2uuONUc^yXlcph&mF5x(E>E}R++LG z0^l#4+uuR*`Xf%LVE2y}^E_hgEG!i?ru4i6f=x3t5L} zhQ<7fJT<%aoMhWPu8x2YZ*b5uE*82^df_wM(Fwefe)=Td*`z`~J9$Q#Ww|T*=v+=i zcXnH86-y>_*qS`A)xncH*lFmOwp##3+l-1tSyPO2hMM{?rBVw|Tk_LPd5whjhoBGh ir6ld+&DX=v%6wcUC3$JOaYiNK$pji)xLg6r7 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 x&|8rhi;B1=M+gG81;BNK`+k~L&6Ln&j4 zG(z@$Cr-Aql$}FHPv<<(d9JQ=J=gWT_`UeP_`SLB?{$At1j$I_!>@;5ArJ`kzmM57 z$j{%;!zBm;0ZcxAyx$ICu##88oQ6PPyBH#D6GMbf!xW1X=p;}65q_`YGSdIIFQPnhgpzAc8GhF0j>Bg1_mZdGv5VXo|{;%U~!OxhS)$UIYSnZ=bfu- z)&bJ{KwiTOb9;5R!io0V!8&y+nq!>bOQ!o?cVCj^lhpZB>(umV0tjQYwvs8Fi4SJN zJxIFAjdy4eO?$H((JiOpUg~Z8uARStntOMBeP?K@r=g}hyU)ZKXQJ|6QFBo8CFJRy z#hkB(SHH-X-Qu5W%Mm^sHWAkc*7H`#MnCjg%tO3pw`Q@dAtfDkL_*tMp&{2)>B!?Mbs_(lMC$jQq-jd4RwwqwM0XCkNlt}vFIm97v|+H=x~I`Y3m1bokUuXY%r*M zOzVM${p0+1%V~!30eZhF-aup=vr_Ge?FoL}oF@DBlk|PZg_4AZCbqhc3vT9{XVXfm zyv@{OZyY@M969`I+^Z>gse5Uqm<`U96Fi@gGDlUrTsoo;6Fh%)i(99vW<%ez^Jkp_ z2>Z_}yZ8pUIl25@=Sf@_1i}DdKq8!u;b3HyVFciSG?W|oSwJ`uzKG@KQ7SrWW+~P2 z9CxEEm;c$6kh35f+cZk+o*H*ZnT*l$w??B$Y4I>Keu+e^9?!?neumI%$lKE zp%kFQp?jA_W5-csA9QaBS3s26P__4zd)#%t$jHU+*(6&DZt1<`Eq8pf3~07qSACht zQM3J~CNAK?b}6nbonDh3v1bod6tU(%m@j@jQ*CffM21W`)Yw?O=@9%XaJZxO{IE*Z z_emku#QhGPpfgdDn^0*=m|;e#m2Rr=WlHp1PuNCb+SZZzo8TR_ZQ&;NiMT?xGrh|; zv{5J8{Ztcjqw6R7_Cr)*h{?jJ{p1i~2X{@E>(&#@f=C_|%WMlhc05prtyO%_=+T{4 zg0WMx#oI6XN}a_(=L0u+qLDTm<;0bn{JDboZ=s2Sc<7jERHv>NIu`rX>XqVx)kANW z)@-T5E)S~39vJ@)8+;X=D>Hm9H9ZsKqUvq1ER?u3Ruo*(LI}xp^?4tUepLY2aS#_| z4?`-IG&dNVZD*3Sou!keXYUsuGIqUBWU+6|0pYTKsYCjHS$5ogZscGIx&{g)V_oph zrjhiCR88l>*?Q~WqhCTQR3-CF5|p``LmYKZ&B^D23#YtqiITSmXTh)U<^wX&Fic{^R{-TxeBf3JN z_qFv=w4TGrS&LddRKnc9f7_L?x)&vg zmgZJ6ekSWs9%h`-MRORpZc2PFsun$n*PmV$lKyB?ZlS>GTRG2$KAQ*~y{EHWKX}Fm ztjmrm)T4wk4jkhWCRcKlb?xud`eu>E%wyIC*I&GUEqd;U?M#E^>Rlc|()sAoN!OF5 zVrjJbQSe14Uvu>bQBoy`ZQWF8)5+5Aty)oeACFRG z^H8bsZdoh-!2Wl!6s4iYTu~cOy9CFHF2}b^S4P@5Ki%d8CR{&yW8Z7_6z13_rEjPWRtxDWzD}$Bz9`?-()%mHqEOlGENO%*+PBmJKm68gjA=^ zmYnpj!9^UG`s-5uO*~ulG6!?(uf*eU3W~oF|7Rp4nA`p@ z@gx%oBZFh{ewI7q{Z$IqJ429lDxAIcL2IJTx^VYA1vLcW z;Irx}GIuYB%XpiGtbj;LIS8Q8s^^q`Ez(Nv3oTZ!4#e7sLv{5c}!db>dh0gQ(6|Gqs9y|vORl? zCxWN9T~NhTVEEf&t;7d#%rBRx^JIcW<(#aC zvV);(u%aVI^BbOF#c=A{$8$%a>+36FZYXY-G(CZVi+ZA zov{J}Cxe1cdFupvh2iaz*Cj@n7g$9C4KvKwDoBMQo637L$+eExwK=LK$KS3izTc0n wz3X{knvGUxQpyw^>A$tO)?$B{Nh|q~3FfA|8FHujKWRj09y~QJc;|TfUpgf0!2kdN literal 3350 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 diff --git a/services/sts/sts-core/src/test/resources/stsstore.jks b/services/sts/sts-core/src/test/resources/stsstore.jks index e805906aadf5055a49caaf72093a8a5312dc7172..51f131f22330e982873f02fbf58b7b946b1444bc 100644 GIT binary patch delta 3471 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 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 QL(N^L6e9NW)j8Mx13&8xn*aa+ literal 5394 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 literal 3350 zcmds(XHZky7RPgvP(u$*Y@sMp#1qg+FT$mW0#a1KhaiR`gh1#`NI)ba0$%W<2!tjm zEubhMNE1Pnat&1}4{6dwM2ZsdC5R5=`0ktc@!k)6_N=wn*>l#c{a?SeKD#~(fk2>) z1>4x!ae>}G-fq}H2n0|-G-J0zSWYV(0#z92Qy>X8&RC`t7!-g)IUq|JRy2&+N(#u$ z${`zN7`4m|04yvJka!o306AD;R&Z7%)XDRIVp8kw4)g+W1P zC<4iCWQo$ldO8q%a99r?v>+(J^uv${e}8`kMj{H1cpO>`6#emo9Dp!_;n&_g19ypV zqtT!usDf5jQbsG;h;S=1P0XhGx5|Z>zO@nn3j{`tWmJOLSF&3Z zuSt-PY_IA{<{9-Azodg0Q#0P%D0J?0s@JN_ThkQnNPRO=!Hf<46Jch8_r(;k1$oy!6e6%2yMhZA}Phb`>RY0}L;LoWCFmt?T^^g#i#Civ$ROB!CCp$*9s!9=I@E(58c%9z`}2FFhvYF+P?^{jx`Q z2S^-bo=d`s-5`+`{>zvQVsSRH;O|r((iXvRG4n&hSwI*7eBD|!hJ0X*YRQH+7aA_r zJjoTepvm^T{>!OLE@f)!`R?HoZK{ol&y35*_icp~iF$)v4i!6gU?qHTy7!9A`0B82 zh|5GpWxcQ^oD$O`l_$S5R5kf=X4I!`c5NAbKF6FLG9vG@F2b>WVN-sOplhScRQTesR%eK}o0P@K9UyU=0UcD`DXBju?!ma^QvO ztNSqP$66@2`m4hVoRO&matXmnhE4&9NW3m@S_w(u8D(IBd~5H-bH$gKh&$_X0hN|) zxWwW7-lKSIgot}5z0kiR?oj7d;KXbEv7*88Q9ZXIQsxAtxU^dT>QcNiNr+|!*mJ&m z)Q~f1Ft#lk|`o z-kCT_RMD>H6pfTMBb2rx;krqJ!Gz;nOMswMZHiD*!l*ozl~@&Q)*y9eES!2MQB>qG z-$J&+=<=dt=?6(rDBddFKeRf4+I8N}{G+0aZ>LVHKS%$}u| zaTWbR?tGB*Fh;SYJtMA7w$1sEFZqk!nR~l~RH|Qi4FzWOh+&6~jpZ~JF`0gabfd7z zcjGnGqctBUAKc)l-t8HyA)Vpo!9HyscGp^)kjt$K@+TKI*V4US_O*)LE-_3gxi(%g z;^i!86eHE!N2$#cl*nn%^o|8!!j$7#73$$Dx({7fIP2Z%FxhQl8q+oQuFtJz15U;W z?azYvRkDo>CW^RCMlp z4@n1FzV_F-obH=6xSm#KU7!`}ZLNIMC8*YEu~p2+wZP1%anJR0@;YDdSpPfdG5K9O z-)zB&_y&KWx@R+|O*%GS_IISg)j$&bFav3n??|g887BL#Us?D`lOXZwe?yuH)*oe! zC%B>c7?RjP8ru~nawX40kGh?>W?w|^Y*+RPw^PiIDUxDzYVx{i8)FR1)EO6go zn=n7q2Wht>)Zc5cx{~!I$#J)G?pQn0^fdVHNgy7!W)r!Ak?5J&+=L}AY+p^;Lh8F$ zQ;`doG^6V1*${bK`??BRa>eLn%W}mU#8P~dqvF1Mc+H;vHvJi6z`tnvQP67VZ=a;& z++%Uj$jk}H14nV+LAaj*LYXZf#Fw&63q(&DX6{>!dJ^?}{HI8NDoFgy1O)?x^C0nk zxZtm1GKekN#HN2p8nP;o8l`8*}mz(Q+50ldU65 z+IfWRbrGSg?Q8P4?$5PtYf+H&o6n2C({A=>XYdyrZtK*z;y%w;8S6APZMn#eaT$RN z^ugmC9elna60M>4=IxJ(8OUPPIGwTBzqJ?m@%ae!&z(i^`z#DYa=7Ci9o${;-aeZ` z#h^h@Q3<28IhJC;!<#1f>#zRm-M`82&CFm;M=BYkce!;KR@7As-|H-!yx36{^14-L ze(qeS16^GvsAz0ZKaZ@aqDzayNu*-E?rGdtZOT?H9WcNbMQeB)wNbeKT)-KkO*_Nh zs1{3|v`bXCHsvq(3OSR`KJcEOGLRB%sZ~>W-Ifhir#zt6jD-Ada-b21YT9`BKPAr` zb@aQ%itF#HKAAWAtrK|LR diff --git a/services/sts/systests/advanced/src/test/resources/stsstore.jks b/services/sts/systests/advanced/src/test/resources/stsstore.jks index e805906aadf5055a49caaf72093a8a5312dc7172..51f131f22330e982873f02fbf58b7b946b1444bc 100644 GIT binary patch delta 3471 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 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 literal 3350 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 diff --git a/services/sts/systests/basic/src/test/resources/stsstore.jks b/services/sts/systests/basic/src/test/resources/stsstore.jks index e805906aadf5055a49caaf72093a8a5312dc7172..51f131f22330e982873f02fbf58b7b946b1444bc 100644 GIT binary patch delta 3471 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 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 literal 3350 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 diff --git a/systests/kerberos/src/test/resources/stsstore.jks b/systests/kerberos/src/test/resources/stsstore.jks index e805906aadf5055a49caaf72093a8a5312dc7172..51f131f22330e982873f02fbf58b7b946b1444bc 100644 GIT binary patch delta 3471 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 Q1~qe@mJi=AP-9>J52ybSc>n+a literal 3978 zcmdtkc{J2(9{})~F}4{qVJzu{LD`qz*iFcmv{)f~ZcJ80uKb|w^`#jJ2o#%VL&-e4p!iR+qFc=I0 z4K8TFVSMyZ{{Vk4S|}8#qbtjwL5nOP3Q+-Ic~B}ma3owAAq0nW!;n!3=2s7oD5ZsK zj?Mgu`PFg3nG(}&r-~j{X*A4UB+jK-9KRj97Cg(csuMKPwRWve$<%Jxm1&kc;B0fe zzoSd$4$+HBUO&SY%rJ<0$HYygWtrmodP%$dH>(FdaUPqUdQx9vgsEoQHNJi54NH`k zI5uUW8WdeQUp8OXPJF#{WJG&Qms-b%{+GL0T)9QGDl#CasMp=2DejBVe>S!4)Z?_b1$HdOnjhSK`Q#6c>chyZq!pY z=eetyTqC>p!m-;)vDS6hxCo%KerNXhjJJfg!@6#FmRoDGV}1FVZb}NnV9yIebi&o? z8E`EaP(1LQyd$FM+wjG75yy-Jc_0SFoYZ_Ix~501Z9`S}*_5EzZw0BlB!)Y)cOENI zE#Xa|w~C}*U;m$h6k6JlT{*t<8ylM|Cy)~@)Z#*)*`rzuFJbrVx)+$07aZ1^H|`ty z`X1%KujPyK&7Idj5Baq}b~bj}8jniV7fY&3O{${h$ewDI&({;U(-XoE8dCOAd#4U= z_cBV?z0{^c{FK6YdAa`H9r(8M$9G{O^&_FP*RsSo{5e{BSWeG;gxJ21tox2n0YAFd*n_=@Yb^#4(zjPSC;szw3=1YtQt^HB|AjJ9Kea;?U`#thY%#@nybu>*cGv8L#En|8?8 ziSFN%d2mkYt?e20$xAA5z6p!-k=pCz&2AQk+teGg?w`v)L#&Aw4)c*omL4P6Ua0z- z_b~^4T77HSVBe`Wkv@{ke&cz&uZZj(UA3@>8zx-w?T_`$dtRInCDycmMR37k@SG^P zFc1aD0+LXbB(X>_q=?fC%zRveolMzIQJ=wgr?1U!G+z&Z-JEqfB-jdo&5=Lc!vsrm z!J=QOJW;C}Mu3wa4#^F0!Qp?bE*jK6Tu`-?BOVu9O;z8`lOZ)LcX~Rzy;3*1iAsZWeS5<_hFG`mi;v!rDBivVBB0NiLE#9?80@C)$CH+hgSTV&26?*D82?a*wSXAs zn_Rdxi#jZQY*7eV0KZxY+;ZL~%fFmGtEJF30CZPLu^9ea()>F_o@w4Pf1i0(PMaGU z80K^&$hQ2#`Dnv&BOcEL3%P`o8J8|QQkyM|1Pf%*H=gH$7WazC z_e~=)ld;BeHS@VJ)xE#hu$t3Z14qd>HLKB6$&aX-iUq#LZJiC4V>{r%S4S%k&9!X# zqHxmt1RW8RJw(;mPG3Pp4MId|6(YW6+@r$rBUagpb8&a$Qj)*K1YZNdubd`>5SakL z@ko&$?O}rTx!|LJN#!74zIWiyd_l-GF^^t0kp>vzO338jsjhAJfIo-&vO+8;Y^P9u;WYSr7>POY+c$EVR>5eg_ zjabp&&CwA&jo(x=u77Nhs8^Q@n#fPS+-Uc*C2ZCSg@?V+bg`(OGW<4%Q}b`F^HEVE4-wfa*RkTE#dkfURXcnW%=&{FAihlNpYtzD0oYX zx3@n*6;NJ6Ms67y^TqT0LsrBA(Ir-}99EW6T2uuONUc^yXlcph&mF5x(E>E}R++LG z0^l#4+uuR*`Xf%LVE2y}^E_hgEG!i?ru4i6f=x3t5L} zhQ<7fJT<%aoMhWPu8x2YZ*b5uE*82^df_wM(Fwefe)=Td*`z`~J9$Q#Ww|T*=v+=i zcXnH86-y>_*qS`A)xncH*lFmOwp##3+l-1tSyPO2hMM{?rBVw|Tk_LPd5whjhoBGh ir6ld+&DX Date: Fri, 6 Nov 2015 21:34:01 +0000 Subject: [PATCH 0021/1346] Trimming the custom OAuth2 client scopes value --- .../cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 0e66e965990..1b9d293653b 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -212,7 +212,7 @@ public void setScopeList(List list) { setScopes(sb.toString()); } public void setScopes(String scopes) { - this.scopes = scopes; + this.scopes = scopes.trim(); } public void setStartUri(String relStartUri) { From b7562ded6c0b54d02615298c975c54585aebf298 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 9 Nov 2015 14:39:51 +0000 Subject: [PATCH 0022/1346] Prototyping the code to let OAuth2 code filter handle access_denied code errors from authenticated users --- .../oauth2/client/AccessDeniedResponse.java | 23 ++++++++++ .../client/ClientCodeRequestFilter.java | 44 +++++++++++++++---- 2 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java new file mode 100644 index 00000000000..9ec28abfde8 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java @@ -0,0 +1,23 @@ +/** + * 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.client; + +public class AccessDeniedResponse { + +} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 1b9d293653b..bd4944543cd 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -65,6 +65,8 @@ public class ClientCodeRequestFilter implements ContainerRequestFilter { private long expiryThreshold; private String redirectUri; private boolean setFormPostResponseMode; + private boolean faultAccessDeniedResponses; + private boolean applicationCanHandleAccessDenied; @Override public void filter(ContainerRequestContext rc) throws IOException { @@ -93,20 +95,34 @@ public void filter(ContainerRequestContext rc) throws IOException { Response codeResponse = createCodeResponse(rc, ui); rc.abortWith(codeResponse); } else if (absoluteRequestUri.endsWith(completeUri)) { - processCodeResponse(rc, ui); - checkSecurityContextEnd(rc); + MultivaluedMap requestParams = toRequestState(rc, ui); + processCodeResponse(rc, ui, requestParams); + checkSecurityContextEnd(rc, requestParams); } } protected void checkSecurityContextStart(ContainerRequestContext rc) { - checkSecurityContextEnd(rc); - } - private void checkSecurityContextEnd(ContainerRequestContext rc) { SecurityContext sc = rc.getSecurityContext(); if (sc == null || sc.getUserPrincipal() == null) { throw ExceptionUtils.toNotAuthorizedException(null, null); } } + private void checkSecurityContextEnd(ContainerRequestContext rc, + MultivaluedMap requestParams) { + String codeParam = requestParams.getFirst(OAuthConstants.AUTHORIZATION_CODE_VALUE); + SecurityContext sc = rc.getSecurityContext(); + if (sc == null || sc.getUserPrincipal() == null) { + if (codeParam == null + && requestParams.containsKey(OAuthConstants.ACCESS_DENIED) + && !faultAccessDeniedResponses) { + if (!applicationCanHandleAccessDenied) { + rc.abortWith(Response.ok(new AccessDeniedResponse()).build()); + } + } else { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } + } + } private Response createCodeResponse(ContainerRequestContext rc, UriInfo ui) { MultivaluedMap redirectState = createRedirectState(rc, ui); @@ -141,15 +157,17 @@ private URI getAbsoluteRedirectUri(UriInfo ui) { return ui.getAbsolutePath(); } } - protected void processCodeResponse(ContainerRequestContext rc, UriInfo ui) { - MultivaluedMap params = toRequestState(rc, ui); - String codeParam = params.getFirst(OAuthConstants.AUTHORIZATION_CODE_VALUE); + protected void processCodeResponse(ContainerRequestContext rc, + UriInfo ui, + MultivaluedMap requestParams) { + + String codeParam = requestParams.getFirst(OAuthConstants.AUTHORIZATION_CODE_VALUE); ClientAccessToken at = null; if (codeParam != null) { AccessTokenGrant grant = new AuthorizationCodeGrant(codeParam, getAbsoluteRedirectUri(ui)); at = OAuthClientUtils.getAccessToken(accessTokenServiceClient, consumer, grant); } - ClientTokenContext tokenContext = initializeClientTokenContext(rc, at, params); + ClientTokenContext tokenContext = initializeClientTokenContext(rc, at, requestParams); if (at != null && clientTokenContextManager != null) { clientTokenContextManager.setClientTokenContext(mc, tokenContext); } @@ -287,4 +305,12 @@ public void setRedirectUri(String redirectUri) { public void setSetFormPostResponseMode(boolean setFormPostResponseMode) { this.setFormPostResponseMode = setFormPostResponseMode; } + + public void setBlockAccessDeniedResponses(boolean blockAccessDeniedResponses) { + this.faultAccessDeniedResponses = blockAccessDeniedResponses; + } + + public void setApplicationCanHandleAccessDenied(boolean applicationCanHandleAccessDenied) { + this.applicationCanHandleAccessDenied = applicationCanHandleAccessDenied; + } } From 1e82efba0035a15afe84ada2d3fd0791733800a7 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 9 Nov 2015 16:33:06 +0000 Subject: [PATCH 0023/1346] Minor update to oauth access_denied check --- .../cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index bd4944543cd..49df2d67028 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -113,7 +113,8 @@ private void checkSecurityContextEnd(ContainerRequestContext rc, SecurityContext sc = rc.getSecurityContext(); if (sc == null || sc.getUserPrincipal() == null) { if (codeParam == null - && requestParams.containsKey(OAuthConstants.ACCESS_DENIED) + && requestParams.containsKey(OAuthConstants.ERROR_KEY) + && OAuthConstants.ACCESS_DENIED.equals(requestParams.get(OAuthConstants.ERROR_KEY)) && !faultAccessDeniedResponses) { if (!applicationCanHandleAccessDenied) { rc.abortWith(Response.ok(new AccessDeniedResponse()).build()); From 27151209eb440ac03876368d443d8df24488cde5 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 9 Nov 2015 11:41:35 +0000 Subject: [PATCH 0024/1346] Renaming SAMLRealm so that it can apply for other token types --- .../provider/AbstractSAMLTokenProvider.java | 4 +- .../sts/token/provider/SAMLTokenProvider.java | 28 +-- .../token/provider/jwt/JWTTokenProvider.java | 38 ++-- .../cxf/sts/token/realm/RealmProperties.java | 185 ++++++++++++++++++ .../apache/cxf/sts/token/realm/SAMLRealm.java | 164 +--------------- .../sts/token/renewer/SAMLTokenRenewer.java | 24 +-- .../mapper/JexlIssueSamlClaimsTest.java | 16 +- .../operation/IssueOnbehalfofUnitTest.java | 16 +- .../operation/IssueSamlClaimsUnitTest.java | 16 +- .../sts/operation/IssueSamlRealmUnitTest.java | 18 +- .../ValidateTokenTransformationUnitTest.java | 16 +- .../token/provider/SAMLProviderRealmTest.java | 69 ++++++- .../renewer/SAMLTokenRenewerRealmTest.java | 16 +- .../SAMLTokenValidatorCachedRealmTest.java | 12 +- .../SAMLTokenValidatorRealmTest.java | 12 +- .../cxf/systest/sts/realms/cxf-sts-saml1.xml | 4 +- .../cxf/systest/sts/realms/cxf-sts-saml2.xml | 2 +- .../systest/sts/issueunit/IssueUnitTest.java | 4 +- .../sts/delegation/cxf-sts-transport.xml | 4 +- .../systest/sts/deployment/cxf-transport.xml | 4 +- .../sts/deployment/stax-cxf-transport.xml | 4 +- .../systest/sts/issuer/cxf-sts-transport.xml | 4 +- 22 files changed, 378 insertions(+), 282 deletions(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/RealmProperties.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/AbstractSAMLTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/AbstractSAMLTokenProvider.java index ed33eb8811e..b5ad4cb8008 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/AbstractSAMLTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/AbstractSAMLTokenProvider.java @@ -29,7 +29,7 @@ import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.SignatureProperties; import org.apache.cxf.sts.request.KeyRequirements; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.ext.WSPasswordCallback; import org.apache.wss4j.common.saml.SamlAssertionWrapper; @@ -43,7 +43,7 @@ public abstract class AbstractSAMLTokenProvider { protected void signToken( SamlAssertionWrapper assertion, - SAMLRealm samlRealm, + RealmProperties samlRealm, STSPropertiesMBean stsProperties, KeyRequirements keyRequirements ) throws Exception { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java index aafe972d585..3d5d76260ea 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java @@ -20,6 +20,7 @@ package org.apache.cxf.sts.token.provider; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,7 +37,7 @@ import org.apache.cxf.sts.claims.ClaimsAttributeStatementProvider; import org.apache.cxf.sts.request.KeyRequirements; import org.apache.cxf.sts.request.TokenRequirements; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.ws.security.sts.provider.STSException; import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.wss4j.common.saml.SAMLCallback; @@ -64,7 +65,7 @@ public class SAMLTokenProvider extends AbstractSAMLTokenProvider implements Toke private SubjectProvider subjectProvider = new DefaultSubjectProvider(); private ConditionsProvider conditionsProvider = new DefaultConditionsProvider(); private boolean signToken = true; - private Map realmMap = new HashMap<>(); + private Map realmMap = new HashMap<>(); private SamlCustomHandler samlCustomHandler; /** @@ -262,19 +263,22 @@ public void setSignToken(boolean signToken) { } /** - * Set the map of realm->SAMLRealm for this token provider - * @param realms the map of realm->SAMLRealm for this token provider + * Set the map of realm->RealmProperties for this token provider + * @param realms the map of realm->RealmProperties for this token provider */ - public void setRealmMap(Map realms) { - this.realmMap = realms; + public void setRealmMap(Map realms) { + this.realmMap.clear(); + if (realms != null) { + this.realmMap.putAll(realms); + } } /** - * Get the map of realm->SAMLRealm for this token provider - * @return the map of realm->SAMLRealm for this token provider + * Get the map of realm->RealmProperties for this token provider + * @return the map of realm->RealmProperties for this token provider */ - public Map getRealmMap() { - return realmMap; + public Map getRealmMap() { + return Collections.unmodifiableMap(realmMap); } public void setSamlCustomHandler(SamlCustomHandler samlCustomHandler) { @@ -285,7 +289,7 @@ private SamlAssertionWrapper createSamlToken( TokenProviderParameters tokenParameters, byte[] secret, Document doc ) throws Exception { String realm = tokenParameters.getRealm(); - SAMLRealm samlRealm = null; + RealmProperties samlRealm = null; if (realm != null && realmMap.containsKey(realm)) { samlRealm = realmMap.get(realm); } @@ -310,7 +314,7 @@ private SamlAssertionWrapper createSamlToken( } public SamlCallbackHandler createCallbackHandler( - TokenProviderParameters tokenParameters, byte[] secret, SAMLRealm samlRealm, Document doc + TokenProviderParameters tokenParameters, byte[] secret, RealmProperties samlRealm, Document doc ) throws Exception { boolean statementAdded = false; diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java index 573788bbd25..6096649cfe2 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -20,6 +20,7 @@ package org.apache.cxf.sts.token.provider.jwt; import java.security.KeyStore; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -45,7 +46,7 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.ws.security.sts.provider.STSException; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.Merlin; @@ -60,7 +61,7 @@ public class JWTTokenProvider implements TokenProvider { private static final Logger LOG = LogUtils.getL7dLogger(JWTTokenProvider.class); private boolean signToken = true; - private Map realmMap = new HashMap<>(); + private Map realmMap = new HashMap<>(); private JWTClaimsProvider jwtClaimsProvider = new DefaultJWTClaimsProvider(); /** @@ -165,19 +166,20 @@ public void setSignToken(boolean signToken) { } /** - * Set the map of realm->SAMLRealm for this token provider - * @param realms the map of realm->SAMLRealm for this token provider + * Set the map of realm->RealmProperties for this token provider + * @param realms the map of realm->RealmProperties for this token provider */ - public void setRealmMap(Map realms) { - this.realmMap = realms; + public void setRealmMap(Map realms) { + this.realmMap.clear(); + this.realmMap.putAll(realms); } /** - * Get the map of realm->SAMLRealm for this token provider - * @return the map of realm->SAMLRealm for this token provider + * Get the map of realm->RealmProperties for this token provider + * @return the map of realm->RealmProperties for this token provider */ - public Map getRealmMap() { - return realmMap; + public Map getRealmMap() { + return Collections.unmodifiableMap(realmMap); } public JWTClaimsProvider getJwtClaimsProvider() { @@ -190,7 +192,7 @@ public void setJwtClaimsProvider(JWTClaimsProvider jwtClaimsProvider) { private String signToken( JwtToken token, - SAMLRealm samlRealm, + RealmProperties jwtRealm, STSPropertiesMBean stsProperties, TokenRequirements tokenRequirements ) throws Exception { @@ -204,18 +206,18 @@ private String signToken( SignatureProperties signatureProperties = stsProperties.getSignatureProperties(); String alias = stsProperties.getSignatureUsername(); - if (samlRealm != null) { + if (jwtRealm != null) { // If SignatureCrypto configured in realm then // callbackhandler and alias of STSPropertiesMBean is ignored - if (samlRealm.getSignatureCrypto() != null) { + if (jwtRealm.getSignatureCrypto() != null) { LOG.fine("SAMLRealm signature keystore used"); - signatureCrypto = samlRealm.getSignatureCrypto(); - callbackHandler = samlRealm.getCallbackHandler(); - alias = samlRealm.getSignatureAlias(); + signatureCrypto = jwtRealm.getSignatureCrypto(); + callbackHandler = jwtRealm.getCallbackHandler(); + alias = jwtRealm.getSignatureAlias(); } // SignatureProperties can be defined independently of SignatureCrypto - if (samlRealm.getSignatureProperties() != null) { - signatureProperties = samlRealm.getSignatureProperties(); + if (jwtRealm.getSignatureProperties() != null) { + signatureProperties = jwtRealm.getSignatureProperties(); } } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/RealmProperties.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/RealmProperties.java new file mode 100644 index 00000000000..3f5112fce52 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/RealmProperties.java @@ -0,0 +1,185 @@ +/** + * 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.sts.token.realm; + +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.security.auth.callback.CallbackHandler; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.rt.security.utils.SecurityUtils; +import org.apache.cxf.sts.SignatureProperties; +import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; + + +/** + * This class defines some properties that are associated with a realm for issuing or validating a particular token. + */ +public class RealmProperties { + + private static final Logger LOG = LogUtils.getL7dLogger(RealmProperties.class); + + private String issuer; + private String signatureAlias; + private Crypto signatureCrypto; + private SignatureProperties signatureProperties; + private String signaturePropertiesFile; + private String callbackHandlerClass; + private CallbackHandler callbackHandler; + + /** + * Get the issuer of this realm + * @return the issuer of this realm + */ + public String getIssuer() { + return issuer; + } + + /** + * Set the issuer of this realm + * @param issuer the issuer of this realm + */ + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + /** + * Get the signature alias to use for this realm + * @return the signature alias to use for this realm + */ + public String getSignatureAlias() { + return signatureAlias; + } + + /** + * Set the signature alias to use for this realm + * @param signatureAlias the signature alias to use for this realm + */ + public void setSignatureAlias(String signatureAlias) { + this.signatureAlias = signatureAlias; + } + + /** + * Set the signature Crypto object + * @param signatureCrypto the signature Crypto object + */ + public void setSignatureCrypto(Crypto signatureCrypto) { + this.signatureCrypto = signatureCrypto; + } + + /** + * Set the String corresponding to the signature Properties class + * @param signaturePropertiesFile the String corresponding to the signature properties file + */ + public void setSignaturePropertiesFile(String signaturePropertiesFile) { + this.signaturePropertiesFile = signaturePropertiesFile; + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Setting signature properties: " + signaturePropertiesFile); + } + } + + /** + * Set the SignatureProperties to use. + * @param signatureProperties the SignatureProperties to use. + */ + public void setSignatureProperties(SignatureProperties signatureProperties) { + this.signatureProperties = signatureProperties; + } + + /** + * Get the SignatureProperties to use. + * @return the SignatureProperties to use. + */ + public SignatureProperties getSignatureProperties() { + return signatureProperties; + } + + + /** + * Get the signature Crypto object + * @return the signature Crypto object + */ + public Crypto getSignatureCrypto() { + if (signatureCrypto == null && signaturePropertiesFile != null) { + Properties sigProperties = SecurityUtils.loadProperties(signaturePropertiesFile); + if (sigProperties == null) { + LOG.fine("Cannot load signature properties using: " + signaturePropertiesFile); + throw new STSException("Configuration error: cannot load signature properties"); + } + try { + signatureCrypto = CryptoFactory.getInstance(sigProperties); + } catch (WSSecurityException ex) { + LOG.fine("Error in loading the signature Crypto object: " + ex.getMessage()); + throw new STSException(ex.getMessage()); + } + } + + return signatureCrypto; + } + + + /** + * Set the CallbackHandler object. + * @param callbackHandler the CallbackHandler object. + */ + public void setCallbackHandler(CallbackHandler callbackHandler) { + this.callbackHandler = callbackHandler; + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Setting callbackHandler: " + callbackHandler); + } + } + + /** + * Set the String corresponding to the CallbackHandler class. + * @param callbackHandlerClass the String corresponding to the CallbackHandler class. + */ + public void setCallbackHandlerClass(String callbackHandlerClass) { + this.callbackHandlerClass = callbackHandlerClass; + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Setting callbackHandlerClass: " + callbackHandlerClass); + } + } + + /** + * Get the CallbackHandler object. + * @return the CallbackHandler object. + */ + public CallbackHandler getCallbackHandler() { + if (callbackHandler == null && callbackHandlerClass != null) { + try { + callbackHandler = SecurityUtils.getCallbackHandler(callbackHandlerClass); + if (callbackHandler == null) { + LOG.fine("Cannot load CallbackHandler using: " + callbackHandlerClass); + throw new STSException("Configuration error: cannot load callback handler"); + } + } catch (Exception ex) { + LOG.fine("Error in loading the callback handler object: " + ex.getMessage()); + throw new STSException(ex.getMessage()); + } + } + return callbackHandler; + } + +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java index 37c4ce65ff0..fe2be7490ce 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java @@ -19,168 +19,10 @@ package org.apache.cxf.sts.token.realm; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.security.auth.callback.CallbackHandler; - -import org.apache.cxf.common.logging.LogUtils; -import org.apache.cxf.rt.security.utils.SecurityUtils; -import org.apache.cxf.sts.SignatureProperties; -import org.apache.cxf.ws.security.sts.provider.STSException; -import org.apache.wss4j.common.crypto.Crypto; -import org.apache.wss4j.common.crypto.CryptoFactory; -import org.apache.wss4j.common.ext.WSSecurityException; - - /** - * This class defines some properties that are associated with a realm for the SAMLTokenProvider and - * SAMLTokenValidator. + * Use RealmProperties intead */ -public class SAMLRealm { - - private static final Logger LOG = LogUtils.getL7dLogger(SAMLRealm.class); - - private String issuer; - private String signatureAlias; - private Crypto signatureCrypto; - private SignatureProperties signatureProperties; - private String signaturePropertiesFile; - private String callbackHandlerClass; - private CallbackHandler callbackHandler; - - /** - * Get the issuer of this SAML realm - * @return the issuer of this SAML realm - */ - public String getIssuer() { - return issuer; - } - - /** - * Set the issuer of this SAML realm - * @param issuer the issuer of this SAML realm - */ - public void setIssuer(String issuer) { - this.issuer = issuer; - } - - /** - * Get the signature alias to use for this SAML realm - * @return the signature alias to use for this SAML realm - */ - public String getSignatureAlias() { - return signatureAlias; - } - - /** - * Set the signature alias to use for this SAML realm - * @param signatureAlias the signature alias to use for this SAML realm - */ - public void setSignatureAlias(String signatureAlias) { - this.signatureAlias = signatureAlias; - } - - /** - * Set the signature Crypto object - * @param signatureCrypto the signature Crypto object - */ - public void setSignatureCrypto(Crypto signatureCrypto) { - this.signatureCrypto = signatureCrypto; - } - - /** - * Set the String corresponding to the signature Properties class - * @param signaturePropertiesFile the String corresponding to the signature properties file - */ - public void setSignaturePropertiesFile(String signaturePropertiesFile) { - this.signaturePropertiesFile = signaturePropertiesFile; - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Setting signature properties: " + signaturePropertiesFile); - } - } - - /** - * Set the SignatureProperties to use. - * @param signatureProperties the SignatureProperties to use. - */ - public void setSignatureProperties(SignatureProperties signatureProperties) { - this.signatureProperties = signatureProperties; - } - - /** - * Get the SignatureProperties to use. - * @return the SignatureProperties to use. - */ - public SignatureProperties getSignatureProperties() { - return signatureProperties; - } - - - /** - * Get the signature Crypto object - * @return the signature Crypto object - */ - public Crypto getSignatureCrypto() { - if (signatureCrypto == null && signaturePropertiesFile != null) { - Properties sigProperties = SecurityUtils.loadProperties(signaturePropertiesFile); - if (sigProperties == null) { - LOG.fine("Cannot load signature properties using: " + signaturePropertiesFile); - throw new STSException("Configuration error: cannot load signature properties"); - } - try { - signatureCrypto = CryptoFactory.getInstance(sigProperties); - } catch (WSSecurityException ex) { - LOG.fine("Error in loading the signature Crypto object: " + ex.getMessage()); - throw new STSException(ex.getMessage()); - } - } - - return signatureCrypto; - } - - - /** - * Set the CallbackHandler object. - * @param callbackHandler the CallbackHandler object. - */ - public void setCallbackHandler(CallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Setting callbackHandler: " + callbackHandler); - } - } - - /** - * Set the String corresponding to the CallbackHandler class. - * @param callbackHandlerClass the String corresponding to the CallbackHandler class. - */ - public void setCallbackHandlerClass(String callbackHandlerClass) { - this.callbackHandlerClass = callbackHandlerClass; - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Setting callbackHandlerClass: " + callbackHandlerClass); - } - } - - /** - * Get the CallbackHandler object. - * @return the CallbackHandler object. - */ - public CallbackHandler getCallbackHandler() { - if (callbackHandler == null && callbackHandlerClass != null) { - try { - callbackHandler = SecurityUtils.getCallbackHandler(callbackHandlerClass); - if (callbackHandler == null) { - LOG.fine("Cannot load CallbackHandler using: " + callbackHandlerClass); - throw new STSException("Configuration error: cannot load callback handler"); - } - } catch (Exception ex) { - LOG.fine("Error in loading the callback handler object: " + ex.getMessage()); - throw new STSException(ex.getMessage()); - } - } - return callbackHandler; - } +@Deprecated +public class SAMLRealm extends RealmProperties { } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewer.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewer.java index 13df377adc9..50759465050 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewer.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewer.java @@ -23,6 +23,7 @@ import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -47,7 +48,7 @@ import org.apache.cxf.sts.token.provider.ConditionsProvider; import org.apache.cxf.sts.token.provider.DefaultConditionsProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.ws.security.sts.provider.STSException; import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.cxf.ws.security.tokenstore.TokenStore; @@ -85,7 +86,7 @@ public class SAMLTokenRenewer extends AbstractSAMLTokenProvider implements Token private static final Logger LOG = LogUtils.getL7dLogger(SAMLTokenRenewer.class); private boolean signToken = true; private ConditionsProvider conditionsProvider = new DefaultConditionsProvider(); - private Map realmMap = new HashMap<>(); + private Map realmMap = new HashMap<>(); private long maxExpiry = DEFAULT_MAX_EXPIRY; // boolean to enable/disable the check of proof of possession private boolean verifyProofOfPossession = true; @@ -265,19 +266,20 @@ public void setSignToken(boolean signToken) { } /** - * Set the map of realm->SAMLRealm for this token provider - * @param realms the map of realm->SAMLRealm for this token provider + * Set the map of realm->RealmProperties for this token provider + * @param realms the map of realm->RealmProperties for this token provider */ - public void setRealmMap(Map realms) { - this.realmMap = realms; + public void setRealmMap(Map realms) { + this.realmMap.clear(); + this.realmMap.putAll(realms); } /** - * Get the map of realm->SAMLRealm for this token provider - * @return the map of realm->SAMLRealm for this token provider + * Get the map of realm->RealmProperties for this token provider + * @return the map of realm->RealmProperties for this token provider */ - public Map getRealmMap() { - return realmMap; + public Map getRealmMap() { + return Collections.unmodifiableMap(realmMap); } private void validateAssertion( @@ -426,7 +428,7 @@ private void signAssertion( if (signToken) { STSPropertiesMBean stsProperties = tokenParameters.getStsProperties(); String realm = tokenParameters.getRealm(); - SAMLRealm samlRealm = null; + RealmProperties samlRealm = null; if (realm != null && realmMap.containsKey(realm)) { samlRealm = realmMap.get(realm); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java index f43ba17cbbc..219f3f98db6 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/claims/mapper/JexlIssueSamlClaimsTest.java @@ -64,8 +64,8 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.realm.Relationship; -import org.apache.cxf.sts.token.realm.SAMLRealm; import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; import org.apache.cxf.sts.token.validator.SAMLTokenValidator; import org.apache.cxf.sts.token.validator.TokenValidator; @@ -124,7 +124,7 @@ private void addService(TokenIssueOperation issueOperation) { public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() throws Exception { TokenIssueOperation issueOperation = new TokenIssueOperation(); - Map realms = createSamlRealms(); + Map realms = createSamlRealms(); // Add Token Provider List providerList = new ArrayList(); @@ -203,7 +203,7 @@ public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() thr assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); } - private RequestSecurityTokenType createRequest(Map realms, Crypto crypto) + private RequestSecurityTokenType createRequest(Map realms, Crypto crypto) throws WSSecurityException { RequestSecurityTokenType request = new RequestSecurityTokenType(); JAXBElement tokenType = new JAXBElement(QNameConstants.TOKEN_TYPE, String.class, @@ -299,13 +299,13 @@ private Element createClaimsType(Document doc, URI claimTypeURI) { return claimType; } - private Map createSamlRealms() { + private Map createSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; @@ -315,7 +315,7 @@ private Map createSamlRealms() { * Mock up an SAML assertion element */ private Element createSAMLAssertion(String tokenType, Crypto crypto, String signatureUsername, - CallbackHandler callbackHandler, Map realms) throws WSSecurityException { + CallbackHandler callbackHandler, Map realms) throws WSSecurityException { SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); samlTokenProvider.setRealmMap(realms); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java index 6a2354d742f..65380cddb43 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueOnbehalfofUnitTest.java @@ -33,6 +33,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; + import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.jaxws.context.WebServiceContextImpl; import org.apache.cxf.jaxws.context.WrappedMessageContext; @@ -63,7 +64,7 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; import org.apache.cxf.sts.token.validator.SAMLTokenValidator; import org.apache.cxf.sts.token.validator.TokenValidator; @@ -88,7 +89,6 @@ import org.apache.wss4j.common.util.DOM2Writer; import org.apache.wss4j.dom.WSConstants; - /** * Some unit tests for the issue operation. */ @@ -920,7 +920,7 @@ public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealm() throws Exception stsProperties.setIdentityMapper(new CustomIdentityMapper()); issueOperation.setStsProperties(stsProperties); - Map realms = createSamlRealms(); + Map realms = createSamlRealms(); // Mock up a request RequestSecurityTokenType request = new RequestSecurityTokenType(); @@ -1224,7 +1224,7 @@ private Element createSAMLAssertion( */ private Element createSAMLAssertion( String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler, - Map realms, String keyType + Map realms, String keyType ) throws WSSecurityException { SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); samlTokenProvider.setRealmMap(realms); @@ -1315,13 +1315,13 @@ private JAXBElement createUsernameToken(String name, String p return tokenType; } - private Map createSamlRealms() { + private Map createSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java index acd061ab032..6eb6db6a6c6 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlClaimsUnitTest.java @@ -66,8 +66,8 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.realm.Relationship; -import org.apache.cxf.sts.token.realm.SAMLRealm; import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; import org.apache.cxf.sts.token.validator.SAMLTokenValidator; import org.apache.cxf.sts.token.validator.TokenValidator; @@ -421,7 +421,7 @@ public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() throws Exception { TokenIssueOperation issueOperation = new TokenIssueOperation(); - Map realms = createSamlRealms(); + Map realms = createSamlRealms(); // Add Token Provider List providerList = new ArrayList(); @@ -569,7 +569,7 @@ private void runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( boolean useGlobalIdentityMapper) throws WSSecurityException { TokenIssueOperation issueOperation = new TokenIssueOperation(); - Map realms = createSamlRealms(); + Map realms = createSamlRealms(); // Add Token Provider List providerList = new ArrayList(); @@ -826,13 +826,13 @@ private Element createClaimValue(Document doc) { return claimValue; } - private Map createSamlRealms() { + private Map createSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; @@ -843,7 +843,7 @@ private Map createSamlRealms() { */ private Element createSAMLAssertion( String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler, - Map realms + Map realms ) throws WSSecurityException { SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java index 6667227d064..f8badb06f21 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java @@ -46,7 +46,7 @@ import org.apache.cxf.sts.service.StaticService; import org.apache.cxf.sts.token.provider.SAMLTokenProvider; import org.apache.cxf.sts.token.provider.TokenProvider; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.ws.security.sts.provider.STSException; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; @@ -332,8 +332,8 @@ public void testIssueSaml1TokenRealmBCustomCrypto() throws Exception { issueOperation.setStsProperties(stsProperties); // Set signature properties in SAMLRealm B - Map samlRealms = provider.getRealmMap(); - SAMLRealm realm = samlRealms.get("B"); + Map samlRealms = provider.getRealmMap(); + RealmProperties realm = samlRealms.get("B"); realm.setSignatureCrypto(crypto); realm.setCallbackHandler(new PasswordCallbackHandler()); @@ -431,8 +431,8 @@ public void testIssueSaml1TokenRealmBCustomCryptoPKCS12() throws Exception { issueOperation.setStsProperties(stsProperties); // Set signature properties in SAMLRealm B - Map samlRealms = provider.getRealmMap(); - SAMLRealm realm = samlRealms.get("B"); + Map samlRealms = provider.getRealmMap(); + RealmProperties realm = samlRealms.get("B"); realm.setSignatureCrypto(CryptoFactory.getInstance(getEncryptionPropertiesPKCS12())); realm.setCallbackHandler(new PasswordCallbackHandler()); @@ -485,12 +485,12 @@ public void testIssueSaml1TokenRealmBCustomCryptoPKCS12() throws Exception { /** * Create some SAML Realms */ - private Map createRealms() { - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + private Map createRealms() { + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java index 857ea55e7a5..8ff98c5da3b 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateTokenTransformationUnitTest.java @@ -61,8 +61,8 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.realm.Relationship; -import org.apache.cxf.sts.token.realm.SAMLRealm; import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; import org.apache.cxf.sts.token.validator.SAMLTokenValidator; import org.apache.cxf.sts.token.validator.TokenValidator; @@ -325,7 +325,7 @@ public void testValidateSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentityR public void testValidateSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() throws Exception { TokenValidateOperation validateOperation = new TokenValidateOperation(); - Map realms = createSamlRealms(); + Map realms = createSamlRealms(); // Add Token Provider List providerList = new ArrayList(); @@ -549,7 +549,7 @@ private void runValidateSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( boolean useGlobalIdentityMapper) throws WSSecurityException { TokenValidateOperation validateOperation = new TokenValidateOperation(); - Map realms = createSamlRealms(); + Map realms = createSamlRealms(); // Add Token Provider List providerList = new ArrayList(); @@ -715,13 +715,13 @@ private STSPropertiesMBean createSTSPropertiesMBean(Crypto crypto) throws WSSecu return stsProperties; } - private Map createSamlRealms() { + private Map createSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; @@ -836,7 +836,7 @@ private Element createClaimsType(Document doc) { */ private Element createSAMLAssertion( String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler, - Map realms + Map realms ) throws WSSecurityException { SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java index 2ef1669d997..81a763c02aa 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLProviderRealmTest.java @@ -33,7 +33,7 @@ import org.apache.cxf.sts.request.KeyRequirements; import org.apache.cxf.sts.request.TokenRequirements; import org.apache.cxf.sts.service.EncryptionProperties; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.ext.WSSecurityException; @@ -57,11 +57,72 @@ public void testRealms() throws Exception { providerParameters.setRealm("A"); // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); + samlRealm.setIssuer("B-Issuer"); + samlRealms.put("B", samlRealm); + ((SAMLTokenProvider)samlTokenProvider).setRealmMap(samlRealms); + + // Realm "A" + assertTrue(samlTokenProvider.canHandleToken(WSConstants.WSS_SAML_TOKEN_TYPE, "A")); + TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + Element token = (Element)providerResponse.getToken(); + String tokenString = DOM2Writer.nodeToString(token); + assertTrue(tokenString.contains(providerResponse.getTokenId())); + assertTrue(tokenString.contains("A-Issuer")); + assertFalse(tokenString.contains("B-Issuer")); + assertFalse(tokenString.contains("STS")); + + // Realm "B" + providerParameters.setRealm("B"); + assertTrue(samlTokenProvider.canHandleToken(WSConstants.WSS_SAML_TOKEN_TYPE, "B")); + providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + token = (Element)providerResponse.getToken(); + tokenString = DOM2Writer.nodeToString(token); + assertTrue(tokenString.contains(providerResponse.getTokenId())); + assertFalse(tokenString.contains("A-Issuer")); + assertTrue(tokenString.contains("B-Issuer")); + assertFalse(tokenString.contains("STS")); + + // Default Realm + providerParameters.setRealm(null); + assertTrue(samlTokenProvider.canHandleToken(WSConstants.WSS_SAML_TOKEN_TYPE, null)); + providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + token = (Element)providerResponse.getToken(); + tokenString = DOM2Writer.nodeToString(token); + assertTrue(tokenString.contains(providerResponse.getTokenId())); + assertFalse(tokenString.contains("A-Issuer")); + assertFalse(tokenString.contains("B-Issuer")); + assertTrue(tokenString.contains("STS")); + } + + @SuppressWarnings("deprecation") + @org.junit.Test + public void testRealmsUsingOldRealmClass() throws Exception { + TokenProvider samlTokenProvider = new SAMLTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(WSConstants.WSS_SAML_TOKEN_TYPE, STSConstants.BEARER_KEY_KEYTYPE); + providerParameters.setRealm("A"); + + // Create Realms + Map samlRealms = + new HashMap(); + org.apache.cxf.sts.token.realm.SAMLRealm samlRealm = new org.apache.cxf.sts.token.realm.SAMLRealm(); + samlRealm.setIssuer("A-Issuer"); + samlRealms.put("A", samlRealm); + samlRealm = new org.apache.cxf.sts.token.realm.SAMLRealm(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); ((SAMLTokenProvider)samlTokenProvider).setRealmMap(samlRealms); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java index afee3714168..6dd4ca8d0d0 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/renewer/SAMLTokenRenewerRealmTest.java @@ -47,7 +47,7 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.realm.SAMLRealmCodec; import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; import org.apache.cxf.sts.token.validator.SAMLTokenValidator; @@ -128,7 +128,7 @@ public void testRealmA() throws Exception { TokenRenewer samlTokenRenewer = new SAMLTokenRenewer(); samlTokenRenewer.setVerifyProofOfPossession(false); samlTokenRenewer.setAllowRenewalAfterExpiry(true); - Map samlRealms = getSamlRealms(); + Map samlRealms = getSamlRealms(); ((SAMLTokenRenewer)samlTokenRenewer).setRealmMap(samlRealms); String realm = validatorResponse.getTokenRealm(); assertTrue(samlTokenRenewer.canHandleToken(validatorResponse.getToken(), realm)); @@ -201,7 +201,7 @@ public void testRealmB() throws Exception { TokenRenewer samlTokenRenewer = new SAMLTokenRenewer(); samlTokenRenewer.setVerifyProofOfPossession(false); samlTokenRenewer.setAllowRenewalAfterExpiry(true); - Map samlRealms = getSamlRealms(); + Map samlRealms = getSamlRealms(); ((SAMLTokenRenewer)samlTokenRenewer).setRealmMap(samlRealms); String realm = validatorResponse.getTokenRealm(); assertTrue(samlTokenRenewer.canHandleToken(validatorResponse.getToken(), realm)); @@ -287,7 +287,7 @@ private Element createSAMLAssertion( } // Create Realms - Map samlRealms = getSamlRealms(); + Map samlRealms = getSamlRealms(); ((SAMLTokenProvider)samlTokenProvider).setRealmMap(samlRealms); TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); @@ -297,13 +297,13 @@ private Element createSAMLAssertion( return (Element)providerResponse.getToken(); } - private Map getSamlRealms() { + private Map getSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java index c12f1c7327b..94d4b2d55f8 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorCachedRealmTest.java @@ -44,7 +44,7 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.realm.SAMLRealmCodec; import org.apache.cxf.ws.security.tokenstore.TokenStore; import org.apache.wss4j.common.crypto.Crypto; @@ -179,7 +179,7 @@ private Element createSAMLAssertion( providerParameters.setRealm(realm); // Create Realms - Map samlRealms = getSamlRealms(); + Map samlRealms = getSamlRealms(); ((SAMLTokenProvider)samlTokenProvider).setRealmMap(samlRealms); TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); @@ -189,13 +189,13 @@ private Element createSAMLAssertion( return (Element)providerResponse.getToken(); } - private Map getSamlRealms() { + private Map getSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java index 08e2551205b..cab5fa76b15 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/SAMLTokenValidatorRealmTest.java @@ -43,7 +43,7 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.sts.token.realm.SAMLRealmCodec; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; @@ -188,7 +188,7 @@ private Element createSAMLAssertion( providerParameters.setRealm(realm); // Create Realms - Map samlRealms = getSamlRealms(); + Map samlRealms = getSamlRealms(); ((SAMLTokenProvider)samlTokenProvider).setRealmMap(samlRealms); TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); @@ -198,13 +198,13 @@ private Element createSAMLAssertion( return (Element)providerResponse.getToken(); } - private Map getSamlRealms() { + private Map getSamlRealms() { // Create Realms - Map samlRealms = new HashMap(); - SAMLRealm samlRealm = new SAMLRealm(); + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); samlRealm.setIssuer("A-Issuer"); samlRealms.put("A", samlRealm); - samlRealm = new SAMLRealm(); + samlRealm = new RealmProperties(); samlRealm.setIssuer("B-Issuer"); samlRealms.put("B", samlRealm); return samlRealms; diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml1.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml1.xml index 2f6a46b4b80..37755343c43 100644 --- a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml1.xml +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml1.xml @@ -44,11 +44,11 @@ - + - + diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml2.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml2.xml index 0cd889e5db6..4447d9f8c9a 100644 --- a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml2.xml +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms/cxf-sts-saml2.xml @@ -44,7 +44,7 @@ - + diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java index d3ea7385638..fc770a8e938 100644 --- a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java +++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/issueunit/IssueUnitTest.java @@ -45,7 +45,7 @@ import org.apache.cxf.sts.token.provider.SAMLTokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; -import org.apache.cxf.sts.token.realm.SAMLRealm; +import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.systest.sts.common.CommonCallbackHandler; import org.apache.cxf.systest.sts.common.SecurityTestUtil; import org.apache.cxf.systest.sts.deployment.STSServer; @@ -554,7 +554,7 @@ private Properties getEncryptionProperties() { */ private Element createSAMLAssertion( String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler, - Map realms, String user, String issuer + Map realms, String user, String issuer ) throws WSSecurityException { SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); samlTokenProvider.setRealmMap(realms); diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/delegation/cxf-sts-transport.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/delegation/cxf-sts-transport.xml index d18ac2ecd96..6ed9b6b4507 100644 --- a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/delegation/cxf-sts-transport.xml +++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/delegation/cxf-sts-transport.xml @@ -83,10 +83,10 @@ https://localhost:(\d)*/doubleit/services/doubleittransport.* - + - + diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-transport.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-transport.xml index 0c45652cbc8..d5201cc24f6 100644 --- a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-transport.xml +++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-transport.xml @@ -96,10 +96,10 @@ https://localhost:(\d)*/doubleit/services/doubleittransport.* - + - + diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/stax-cxf-transport.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/stax-cxf-transport.xml index 89722131724..9f05ebb18a6 100644 --- a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/stax-cxf-transport.xml +++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/deployment/stax-cxf-transport.xml @@ -84,10 +84,10 @@ https://localhost:(\d)*/doubleit/services/doubleittransport.* - + - + diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/issuer/cxf-sts-transport.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/issuer/cxf-sts-transport.xml index 99229cde1ba..762ee45fc7c 100644 --- a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/issuer/cxf-sts-transport.xml +++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/issuer/cxf-sts-transport.xml @@ -84,10 +84,10 @@ https://localhost:(\d)*/doubleit/services/doubleittransport.* - + - + From 512e9e900f3c840e1b0ab4c2fb4c3950bffc84ad Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 9 Nov 2015 12:09:59 +0000 Subject: [PATCH 0025/1346] Adding some realms tests --- .../jwt/DefaultJWTClaimsProvider.java | 11 +- .../jwt/JWTClaimsProviderParameters.java | 9 + .../token/provider/jwt/JWTTokenProvider.java | 21 +- .../sts/operation/IssueJWTRealmUnitTest.java | 447 ++++++++++++++++++ .../provider/JWTTokenProviderRealmTest.java | 154 ++++++ 5 files changed, 638 insertions(+), 4 deletions(-) create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderRealmTest.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java index 5addb95df4c..9f29e62c2da 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java @@ -27,6 +27,7 @@ import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.request.ReceivedToken; import org.apache.cxf.sts.request.ReceivedToken.STATE; import org.apache.cxf.sts.token.provider.TokenProviderParameters; @@ -49,7 +50,15 @@ public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParam JwtClaims claims = new JwtClaims(); claims.setSubject(getSubjectName(jwtClaimsProviderParameters)); claims.setTokenId(UUID.randomUUID().toString()); - claims.setIssuer("DoubleItSTSIssuer"); + + // Set the Issuer + String issuer = jwtClaimsProviderParameters.getIssuer(); + if (issuer == null) { + STSPropertiesMBean stsProperties = jwtClaimsProviderParameters.getProviderParameters().getStsProperties(); + claims.setIssuer(stsProperties.getIssuer()); + } else { + claims.setIssuer(issuer); + } Date currentDate = new Date(); claims.setIssuedAt(currentDate.getTime() / 1000L); diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java index 24f1ed9f999..300840866e7 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTClaimsProviderParameters.java @@ -27,6 +27,7 @@ public class JWTClaimsProviderParameters { private TokenProviderParameters providerParameters; + private String issuer; public TokenProviderParameters getProviderParameters() { return providerParameters; @@ -35,5 +36,13 @@ public TokenProviderParameters getProviderParameters() { public void setProviderParameters(TokenProviderParameters providerParameters) { this.providerParameters = providerParameters; } + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java index 6096649cfe2..0f5a3830587 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -93,9 +93,18 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters LOG.fine("Handling token of type: " + tokenRequirements.getTokenType()); } + String realm = tokenParameters.getRealm(); + RealmProperties jwtRealm = null; + if (realm != null && realmMap.containsKey(realm)) { + jwtRealm = realmMap.get(realm); + } + // Get the claims JWTClaimsProviderParameters jwtClaimsProviderParameters = new JWTClaimsProviderParameters(); jwtClaimsProviderParameters.setProviderParameters(tokenParameters); + if (jwtRealm != null) { + jwtClaimsProviderParameters.setIssuer(jwtRealm.getIssuer()); + } JwtClaims claims = jwtClaimsProvider.getJwtClaims(jwtClaimsProviderParameters); @@ -127,7 +136,7 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters JwtToken token = new JwtToken(claims); - String tokenData = signToken(token, null, tokenParameters.getStsProperties(), + String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties(), tokenParameters.getTokenRequirements()); TokenProviderResponse response = new TokenProviderResponse(); @@ -243,8 +252,14 @@ private String signToken( String password = cb[0].getPassword(); signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, signatureAlgorithm); - signingProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias); - signingProperties.put(JoseConstants.RSSEC_KEY_PSWD, password); + if (alias != null) { + signingProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias); + } + if (password != null) { + signingProperties.put(JoseConstants.RSSEC_KEY_PSWD, password); + } else { + throw new STSException("Can't get the password", STSException.REQUEST_FAILED); + } if (!(signatureCrypto instanceof Merlin)) { throw new STSException("Can't get the keystore", STSException.REQUEST_FAILED); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java new file mode 100644 index 00000000000..9e2f8a7bb0b --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java @@ -0,0 +1,447 @@ +/** + * 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.sts.operation; + +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 javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.service.ServiceMBean; +import org.apache.cxf.sts.service.StaticService; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.realm.RealmProperties; +import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Some unit tests for the issue operation to issue JWT tokens in a specific realm. + */ +public class IssueJWTRealmUnitTest extends org.junit.Assert { + + public static final QName REQUESTED_SECURITY_TOKEN = + QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(null).getName(); + public static final QName ATTACHED_REFERENCE = + QNameConstants.WS_TRUST_FACTORY.createRequestedAttachedReference(null).getName(); + public static final QName UNATTACHED_REFERENCE = + QNameConstants.WS_TRUST_FACTORY.createRequestedUnattachedReference(null).getName(); + + /** + * Test to successfully issue a JWT token in realm "A". + */ + @org.junit.Test + public void testIssueJWTTokenRealmA() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + JWTTokenProvider provider = new JWTTokenProvider(); + provider.setRealmMap(createRealms()); + providerList.add(provider); + issueOperation.setTokenProviders(providerList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + stsProperties.setRealmParser(new CustomRealmParser()); + issueOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "ldap"); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("A-Issuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); + } + + /** + * Test to successfully issue a JWT token in realm "B". + */ + @org.junit.Test + public void testIssueJWTTokenRealmB() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + JWTTokenProvider provider = new JWTTokenProvider(); + provider.setRealmMap(createRealms()); + providerList.add(provider); + issueOperation.setTokenProviders(providerList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + stsProperties.setRealmParser(new CustomRealmParser()); + issueOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "https"); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("B-Issuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); + } + + /** + * Test to successfully issue a JWT token in the default realm. + */ + @org.junit.Test + public void testIssueJWTTokenDefaultRealm() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + JWTTokenProvider provider = new JWTTokenProvider(); + provider.setRealmMap(createRealms()); + providerList.add(provider); + issueOperation.setTokenProviders(providerList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + stsProperties.setRealmParser(new CustomRealmParser()); + issueOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "unknown"); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("STS", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); + } + + + /** + * Test to successfully issue a JWT token in realm "B" + * using crypto definition in RealmProperties + */ + @org.junit.Test + public void testIssueJWTTokenRealmBCustomCrypto() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + JWTTokenProvider provider = new JWTTokenProvider(); + provider.setRealmMap(createRealms()); + providerList.add(provider); + issueOperation.setTokenProviders(providerList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + stsProperties.setRealmParser(new CustomRealmParser()); + issueOperation.setStsProperties(stsProperties); + + // Set signature properties in Realm B + Map realms = provider.getRealmMap(); + RealmProperties realm = realms.get("B"); + realm.setSignatureCrypto(crypto); + realm.setCallbackHandler(new PasswordCallbackHandler()); + + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "https"); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token - this will fail as the RealmProperties configuration is inconsistent + // no signature alias defined + try { + issueOperation.issue(request, webServiceContext); + fail("Failure expected on no encryption name"); + } catch (STSException ex) { + // expected + } + + realm.setSignatureAlias("mystskey"); + + // Issue a token + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("B-Issuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); + } + + /** + * Create some JWT Realms + */ + private Map createRealms() { + Map realms = new HashMap(); + RealmProperties realm = new RealmProperties(); + realm.setIssuer("A-Issuer"); + realms.put("A", realm); + realm = new RealmProperties(); + realm.setIssuer("B-Issuer"); + realms.put("B", realm); + + return realms; + } + + /* + * Create a security context object + */ + private SecurityContext createSecurityContext(final Principal p) { + return new SecurityContext() { + public Principal getUserPrincipal() { + return p; + } + public boolean isUserInRole(String role) { + return false; + } + }; + } + + /* + * Mock up an AppliesTo element using the supplied address + */ + private Element createAppliesToElement(String addressUrl) { + Document doc = DOMUtils.createDocument(); + Element appliesTo = doc.createElementNS(STSConstants.WSP_NS, "wsp:AppliesTo"); + appliesTo.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsp", STSConstants.WSP_NS); + Element endpointRef = doc.createElementNS(STSConstants.WSA_NS_05, "wsa:EndpointReference"); + endpointRef.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsa", STSConstants.WSA_NS_05); + Element address = doc.createElementNS(STSConstants.WSA_NS_05, "wsa:Address"); + address.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsa", STSConstants.WSA_NS_05); + address.setTextContent(addressUrl); + endpointRef.appendChild(address); + appliesTo.appendChild(endpointRef); + return appliesTo; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderRealmTest.java new file mode 100644 index 00000000000..2a747c14a91 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderRealmTest.java @@ -0,0 +1,154 @@ +/** + * 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.sts.token.provider; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.realm.RealmProperties; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; + +/** + * Some unit tests for creating JWT Tokens via the JWTTokenProvider in different realms + */ +public class JWTTokenProviderRealmTest extends org.junit.Assert { + + @org.junit.Test + public void testRealms() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + TokenProviderParameters providerParameters = createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + providerParameters.setRealm("A"); + + // Create Realms + Map jwtRealms = new HashMap(); + RealmProperties jwtRealm = new RealmProperties(); + jwtRealm.setIssuer("A-Issuer"); + jwtRealms.put("A", jwtRealm); + jwtRealm = new RealmProperties(); + jwtRealm.setIssuer("B-Issuer"); + jwtRealms.put("B", jwtRealm); + ((JWTTokenProvider)jwtTokenProvider).setRealmMap(jwtRealms); + + // Realm "A" + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE, "A")); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUER), "A-Issuer"); + + // Realm "B" + providerParameters.setRealm("B"); + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE, "B")); + providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + token = (String)providerResponse.getToken(); + assertNotNull(token); + jwtConsumer = new JwsJwtCompactConsumer(token); + jwt = jwtConsumer.getJwtToken(); + + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUER), "B-Issuer"); + + // Default Realm + providerParameters.setRealm(null); + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE, null)); + providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + token = (String)providerResponse.getToken(); + assertNotNull(token); + jwtConsumer = new JwsJwtCompactConsumer(token); + jwt = jwtConsumer.getJwtToken(); + + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUER), "STS"); + } + + private TokenProviderParameters createProviderParameters( + String tokenType + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + +} From b4a17ce9970cd124cec61eaed04061a529e2398f Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 9 Nov 2015 12:19:08 +0000 Subject: [PATCH 0026/1346] Fixing merge --- .../org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java index 9e2f8a7bb0b..3a8b4964d51 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java @@ -29,6 +29,9 @@ import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.jaxws.context.WebServiceContextImpl; import org.apache.cxf.jaxws.context.WrappedMessageContext; @@ -56,8 +59,6 @@ import org.apache.wss4j.common.principal.CustomTokenPrincipal; import org.apache.wss4j.dom.WSConstants; import org.junit.Assert; -import org.w3c.dom.Document; -import org.w3c.dom.Element; /** * Some unit tests for the issue operation to issue JWT tokens in a specific realm. From 0848bdb305b5d6e542da417ff5fd9e7c9ace5381 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 9 Nov 2015 14:49:09 +0000 Subject: [PATCH 0027/1346] Adding support for claims in JWT Tokens in the STS --- .../ClaimsAttributeStatementProvider.java | 30 +- .../apache/cxf/sts/claims/ClaimsUtils.java | 67 ++ .../jwt/DefaultJWTClaimsProvider.java | 26 + .../sts/operation/IssueJWTClaimsUnitTest.java | 775 ++++++++++++++++++ .../cxf/sts/token/provider/JWTClaimsTest.java | 288 +++++++ 5 files changed, 1157 insertions(+), 29 deletions(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsUtils.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTClaimsTest.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java index 39469d34c3a..0f1882f5640 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java @@ -36,35 +36,7 @@ public class ClaimsAttributeStatementProvider implements AttributeStatementProvi public AttributeStatementBean getStatement(TokenProviderParameters providerParameters) { // Handle Claims - ClaimsManager claimsManager = providerParameters.getClaimsManager(); - ProcessedClaimCollection retrievedClaims = new ProcessedClaimCollection(); - if (claimsManager != null) { - ClaimsParameters params = new ClaimsParameters(); - params.setAdditionalProperties(providerParameters.getAdditionalProperties()); - params.setAppliesToAddress(providerParameters.getAppliesToAddress()); - params.setEncryptionProperties(providerParameters.getEncryptionProperties()); - params.setKeyRequirements(providerParameters.getKeyRequirements()); - if (providerParameters.getTokenRequirements().getOnBehalfOf() != null) { - params.setPrincipal(providerParameters.getTokenRequirements().getOnBehalfOf().getPrincipal()); - params.setRoles(providerParameters.getTokenRequirements().getOnBehalfOf().getRoles()); - } else if (providerParameters.getTokenRequirements().getActAs() != null) { - params.setPrincipal(providerParameters.getTokenRequirements().getActAs().getPrincipal()); - params.setRoles(providerParameters.getTokenRequirements().getActAs().getRoles()); - } else { - params.setPrincipal(providerParameters.getPrincipal()); - } - params.setRealm(providerParameters.getRealm()); - params.setStsProperties(providerParameters.getStsProperties()); - params.setTokenRequirements(providerParameters.getTokenRequirements()); - params.setTokenStore(providerParameters.getTokenStore()); - params.setWebServiceContext(providerParameters.getWebServiceContext()); - retrievedClaims = - claimsManager.retrieveClaimValues( - providerParameters.getRequestedPrimaryClaims(), - providerParameters.getRequestedSecondaryClaims(), - params - ); - } + ProcessedClaimCollection retrievedClaims = ClaimsUtils.processClaims(providerParameters); if (retrievedClaims == null) { return null; } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsUtils.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsUtils.java new file mode 100644 index 00000000000..fbd7e1cd180 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsUtils.java @@ -0,0 +1,67 @@ +/** + * 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.sts.claims; + +import org.apache.cxf.sts.token.provider.TokenProviderParameters; + +/** + * Some common utility methods for claims + */ +public final class ClaimsUtils { + + private ClaimsUtils() { + // complete + } + + public static ProcessedClaimCollection processClaims(TokenProviderParameters providerParameters) { + // Handle Claims + ClaimsManager claimsManager = providerParameters.getClaimsManager(); + ProcessedClaimCollection retrievedClaims = new ProcessedClaimCollection(); + if (claimsManager != null) { + ClaimsParameters params = new ClaimsParameters(); + params.setAdditionalProperties(providerParameters.getAdditionalProperties()); + params.setAppliesToAddress(providerParameters.getAppliesToAddress()); + params.setEncryptionProperties(providerParameters.getEncryptionProperties()); + params.setKeyRequirements(providerParameters.getKeyRequirements()); + if (providerParameters.getTokenRequirements().getOnBehalfOf() != null) { + params.setPrincipal(providerParameters.getTokenRequirements().getOnBehalfOf().getPrincipal()); + params.setRoles(providerParameters.getTokenRequirements().getOnBehalfOf().getRoles()); + } else if (providerParameters.getTokenRequirements().getActAs() != null) { + params.setPrincipal(providerParameters.getTokenRequirements().getActAs().getPrincipal()); + params.setRoles(providerParameters.getTokenRequirements().getActAs().getRoles()); + } else { + params.setPrincipal(providerParameters.getPrincipal()); + } + params.setRealm(providerParameters.getRealm()); + params.setStsProperties(providerParameters.getStsProperties()); + params.setTokenRequirements(providerParameters.getTokenRequirements()); + params.setTokenStore(providerParameters.getTokenStore()); + params.setWebServiceContext(providerParameters.getWebServiceContext()); + retrievedClaims = + claimsManager.retrieveClaimValues( + providerParameters.getRequestedPrimaryClaims(), + providerParameters.getRequestedSecondaryClaims(), + params + ); + } + return retrievedClaims; + } + +} + diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java index 9f29e62c2da..1e80f960ab3 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java @@ -20,6 +20,7 @@ import java.security.Principal; import java.util.Date; +import java.util.Iterator; import java.util.UUID; import java.util.logging.Logger; @@ -28,6 +29,9 @@ import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.claims.ClaimsUtils; +import org.apache.cxf.sts.claims.ProcessedClaim; +import org.apache.cxf.sts.claims.ProcessedClaimCollection; import org.apache.cxf.sts.request.ReceivedToken; import org.apache.cxf.sts.request.ReceivedToken.STATE; import org.apache.cxf.sts.token.provider.TokenProviderParameters; @@ -60,6 +64,8 @@ public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParam claims.setIssuer(issuer); } + handleWSTrustClaims(jwtClaimsProviderParameters, claims); + Date currentDate = new Date(); claims.setIssuedAt(currentDate.getTime() / 1000L); long currentTime = currentDate.getTime() + 300L * 1000L; @@ -129,6 +135,26 @@ protected Principal getPrincipal(JWTClaimsProviderParameters jwtClaimsProviderPa return principal; } + protected void handleWSTrustClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters, JwtClaims claims) { + TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters(); + + // Handle Claims + ProcessedClaimCollection retrievedClaims = ClaimsUtils.processClaims(providerParameters); + if (retrievedClaims != null) { + Iterator claimIterator = retrievedClaims.iterator(); + while (claimIterator.hasNext()) { + ProcessedClaim claim = claimIterator.next(); + if (claim.getClaimType() != null && claim.getValues() != null && !claim.getValues().isEmpty()) { + Object claimValues = claim.getValues(); + if (claim.getValues().size() == 1) { + claimValues = claim.getValues().get(0); + } + claims.setProperty(claim.getClaimType().toString(), claimValues); + } + } + } + } + public boolean isUseX500CN() { return useX500CN; } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java new file mode 100644 index 00000000000..5e9219141cb --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java @@ -0,0 +1,775 @@ +/** + * 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.sts.operation; + +import java.net.URI; +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 javax.security.auth.callback.CallbackHandler; +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rt.security.claims.Claim; +import org.apache.cxf.rt.security.claims.ClaimCollection; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.claims.ClaimTypes; +import org.apache.cxf.sts.claims.ClaimsAttributeStatementProvider; +import org.apache.cxf.sts.claims.ClaimsHandler; +import org.apache.cxf.sts.claims.ClaimsManager; +import org.apache.cxf.sts.claims.ClaimsMapper; +import org.apache.cxf.sts.common.CustomClaimsHandler; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.service.ServiceMBean; +import org.apache.cxf.sts.service.StaticService; +import org.apache.cxf.sts.token.delegation.SAMLDelegationHandler; +import org.apache.cxf.sts.token.delegation.TokenDelegationHandler; +import org.apache.cxf.sts.token.provider.AttributeStatementProvider; +import org.apache.cxf.sts.token.provider.SAMLTokenProvider; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.realm.RealmProperties; +import org.apache.cxf.sts.token.realm.Relationship; +import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; +import org.apache.cxf.sts.token.validator.SAMLTokenValidator; +import org.apache.cxf.sts.token.validator.TokenValidator; +import org.apache.cxf.ws.security.sts.provider.model.ClaimsType; +import org.apache.cxf.ws.security.sts.provider.model.OnBehalfOfType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.apache.wss4j.common.util.DOM2Writer; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; + +/** + * Some unit tests for the issue operation to issue JWT tokens with Claims information. + */ +public class IssueJWTClaimsUnitTest extends org.junit.Assert { + + public static final QName REQUESTED_SECURITY_TOKEN = + QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(null).getName(); + + private static final URI ROLE_CLAIM = + URI.create("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); + + /** + * Test to successfully issue a JWT token. + */ + @org.junit.Test + public void testIssueJWTToken() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new JWTTokenProvider()); + issueOperation.setTokenProviders(providerList); + + addService(issueOperation); + + addSTSProperties(issueOperation); + + // Set the ClaimsManager + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + issueOperation.setClaimsManager(claimsManager); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + Element secondaryParameters = createSecondaryParameters(); + request.getAny().add(secondaryParameters); + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + WebServiceContextImpl webServiceContext = setupMessageContext(); + + List securityTokenResponse = issueToken(issueOperation, request, + webServiceContext); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); + assertEquals(jwt.getClaim(ROLE_CLAIM.toString()), "administrator"); + } + + /** + * @param issueOperation + * @param request + * @param webServiceContext + * @return + */ + private List issueToken(TokenIssueOperation issueOperation, + RequestSecurityTokenType request, WebServiceContextImpl webServiceContext) { + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + return securityTokenResponse; + } + + /** + * @return + */ + private WebServiceContextImpl setupMessageContext() { + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + return new WebServiceContextImpl(msgCtx); + } + + /** + * @param issueOperation + * @throws WSSecurityException + */ + private void addSTSProperties(TokenIssueOperation issueOperation) throws WSSecurityException { + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + issueOperation.setStsProperties(stsProperties); + } + + /** + * @param issueOperation + */ + private void addService(TokenIssueOperation issueOperation) { + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + } + + /** + * Test to successfully issue a JWT token. The claims information is included as a + * JAXB Element under RequestSecurityToken, rather than as a child of SecondaryParameters. + */ + @org.junit.Test + public void testIssueJaxbJWTToken() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new JWTTokenProvider()); + issueOperation.setTokenProviders(providerList); + + addService(issueOperation); + + addSTSProperties(issueOperation); + + // Set the ClaimsManager + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + issueOperation.setClaimsManager(claimsManager); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Add a ClaimsType + ClaimsType claimsType = new ClaimsType(); + claimsType.setDialect(STSConstants.IDT_NS_05_05); + Document doc = DOMUtils.createDocument(); + Element claimType = createClaimsType(doc); + claimsType.getAny().add(claimType); + + JAXBElement claimsTypeJaxb = + new JAXBElement( + QNameConstants.CLAIMS, ClaimsType.class, claimsType + ); + request.getAny().add(claimsTypeJaxb); + + request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + WebServiceContextImpl webServiceContext = setupMessageContext(); + + List securityTokenResponse = issueToken(issueOperation, request, + webServiceContext); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); + } + + /** + * Test to successfully issue a SAML 2 token (realm "B") on-behalf-of a SAML 2 token + * which was issued by realm "A". + * The relationship type between realm A and B is: FederateClaims + * TODO + */ + @org.junit.Test + @org.junit.Ignore + public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() + throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + Map realms = createSamlRealms(); + + // Add Token Provider + List providerList = new ArrayList(); + SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); + samlTokenProvider.setRealmMap(realms); + List customProviderList = + new ArrayList(); + customProviderList.add(new ClaimsAttributeStatementProvider()); + samlTokenProvider.setAttributeStatementProviders(customProviderList); + providerList.add(samlTokenProvider); + issueOperation.setTokenProviders(providerList); + + TokenDelegationHandler delegationHandler = new SAMLDelegationHandler(); + issueOperation.setDelegationHandlers(Collections.singletonList(delegationHandler)); + + // Add Token Validator + List validatorList = new ArrayList(); + SAMLTokenValidator samlTokenValidator = new SAMLTokenValidator(); + samlTokenValidator.setSamlRealmCodec(new IssuerSAMLRealmCodec()); + validatorList.add(samlTokenValidator); + issueOperation.setTokenValidators(validatorList); + + addService(issueOperation); + + // Add Relationship list + List relationshipList = new ArrayList(); + Relationship rs = createRelationship(); + relationshipList.add(rs); + + // Add STSProperties object + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + STSPropertiesMBean stsProperties = createSTSPropertiesMBean(crypto); + stsProperties.setRealmParser(new CustomRealmParser()); + stsProperties.setIdentityMapper(new CustomIdentityMapper()); + stsProperties.setRelationships(relationshipList); + issueOperation.setStsProperties(stsProperties); + + // Set the ClaimsManager + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + issueOperation.setClaimsManager(claimsManager); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, WSConstants.WSS_SAML2_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Add a ClaimsType + ClaimsType claimsType = new ClaimsType(); + claimsType.setDialect(STSConstants.IDT_NS_05_05); + + Document doc = DOMUtils.createDocument(); + Element claimType = createClaimsType(doc); + claimsType.getAny().add(claimType); + + JAXBElement claimsTypeJaxb = + new JAXBElement( + QNameConstants.CLAIMS, ClaimsType.class, claimsType + ); + request.getAny().add(claimsTypeJaxb); + + //request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // create a SAML Token via the SAMLTokenProvider which contains claims + CallbackHandler callbackHandler = new PasswordCallbackHandler(); + Element samlToken = + createSAMLAssertion(WSConstants.WSS_SAML2_TOKEN_TYPE, crypto, "mystskey", + callbackHandler, realms); + Document docToken = samlToken.getOwnerDocument(); + samlToken = (Element)docToken.appendChild(samlToken); + String samlString = DOM2Writer.nodeToString(samlToken); + assertTrue(samlString.contains("AttributeStatement")); + assertTrue(samlString.contains("alice")); + assertTrue(samlString.contains("doe")); + assertTrue(samlString.contains(SAML2Constants.CONF_BEARER)); + + // add SAML token as On-Behalf-Of element + OnBehalfOfType onbehalfof = new OnBehalfOfType(); + onbehalfof.setAny(samlToken); + JAXBElement onbehalfofType = + new JAXBElement( + QNameConstants.ON_BEHALF_OF, OnBehalfOfType.class, onbehalfof + ); + request.getAny().add(onbehalfofType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "https"); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + List securityTokenResponseList = issueToken(issueOperation, + request, webServiceContext); + RequestSecurityTokenResponseType securityTokenResponse = securityTokenResponseList.get(0); + + // Test the generated token. + Element assertion = null; + for (Object tokenObject : securityTokenResponse.getAny()) { + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + assertion = (Element)rstType.getAny(); + break; + } + } + assertNotNull(assertion); + String tokenString = DOM2Writer.nodeToString(assertion); + assertTrue(tokenString.contains("AttributeStatement")); + assertTrue(tokenString.contains("alice")); //subject unchanged + assertTrue(tokenString.contains("DOE")); //transformed claim (to uppercase) + assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); + } + + /** + * Test to successfully issue a SAML 2 token (realm "B") on-behalf-of a SAML 2 token + * which was issued by realm "A". + * The relationship type between realm A and B is: FederateIdentity + * IdentityMapper is configured globally in STSPropertiesMBean + * TODO + */ + @org.junit.Test + @org.junit.Ignore + public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentityGlobalConfig() + throws Exception { + runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity(true); + } + + + /** + * Test to successfully issue a SAML 2 token (realm "B") on-behalf-of a SAML 2 token + * which was issued by realm "A". + * The relationship type between realm A and B is: FederateIdentity + * IdentityMapper is configured in the Relationship + * TODO + */ + @org.junit.Test + @org.junit.Ignore + public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentityRelationshipConfig() + throws Exception { + runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity(false); + } + + private void runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( + boolean useGlobalIdentityMapper) throws WSSecurityException { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + Map realms = createSamlRealms(); + + // Add Token Provider + List providerList = new ArrayList(); + SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); + samlTokenProvider.setRealmMap(realms); + List customProviderList = + new ArrayList(); + customProviderList.add(new ClaimsAttributeStatementProvider()); + samlTokenProvider.setAttributeStatementProviders(customProviderList); + providerList.add(samlTokenProvider); + issueOperation.setTokenProviders(providerList); + + TokenDelegationHandler delegationHandler = new SAMLDelegationHandler(); + issueOperation.setDelegationHandlers(Collections.singletonList(delegationHandler)); + + // Add Token Validator + List validatorList = new ArrayList(); + SAMLTokenValidator samlTokenValidator = new SAMLTokenValidator(); + samlTokenValidator.setSamlRealmCodec(new IssuerSAMLRealmCodec()); + validatorList.add(samlTokenValidator); + issueOperation.setTokenValidators(validatorList); + + addService(issueOperation); + + // Add Relationship list + List relationshipList = new ArrayList(); + Relationship rs = createRelationship(); + rs.setType(Relationship.FED_TYPE_IDENTITY); + rs.setIdentityMapper(new CustomIdentityMapper()); + relationshipList.add(rs); + + // Add STSProperties object + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + STSPropertiesMBean stsProperties = createSTSPropertiesMBean(crypto); + stsProperties.setRealmParser(new CustomRealmParser()); + + if (useGlobalIdentityMapper) { + stsProperties.setIdentityMapper(new CustomIdentityMapper()); + } else { + stsProperties.setRelationships(relationshipList); + } + + issueOperation.setStsProperties(stsProperties); + + // Set the ClaimsManager + ClaimsManager claimsManager = new ClaimsManager(); + claimsManager.setClaimHandlers(Collections.singletonList((ClaimsHandler)new CustomClaimsHandler())); + issueOperation.setClaimsManager(claimsManager); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, WSConstants.WSS_SAML2_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Add a ClaimsType + ClaimsType claimsType = new ClaimsType(); + claimsType.setDialect(STSConstants.IDT_NS_05_05); + + Document doc = DOMUtils.createDocument(); + Element claimType = createClaimsType(doc); + claimsType.getAny().add(claimType); + + JAXBElement claimsTypeJaxb = + new JAXBElement( + QNameConstants.CLAIMS, ClaimsType.class, claimsType + ); + request.getAny().add(claimsTypeJaxb); + + //request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy")); + + // create a SAML Token via the SAMLTokenProvider which contains claims + CallbackHandler callbackHandler = new PasswordCallbackHandler(); + Element samlToken = + createSAMLAssertion(WSConstants.WSS_SAML2_TOKEN_TYPE, crypto, "mystskey", + callbackHandler, realms); + Document docToken = samlToken.getOwnerDocument(); + samlToken = (Element)docToken.appendChild(samlToken); + String samlString = DOM2Writer.nodeToString(samlToken); + assertTrue(samlString.contains("AttributeStatement")); + assertTrue(samlString.contains("alice")); + assertTrue(samlString.contains("doe")); + assertTrue(samlString.contains(SAML2Constants.CONF_BEARER)); + + // add SAML token as On-Behalf-Of element + OnBehalfOfType onbehalfof = new OnBehalfOfType(); + onbehalfof.setAny(samlToken); + JAXBElement onbehalfofType = + new JAXBElement( + QNameConstants.ON_BEHALF_OF, OnBehalfOfType.class, onbehalfof + ); + request.getAny().add(onbehalfofType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "https"); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + List securityTokenResponseList = issueToken(issueOperation, + request, webServiceContext); + RequestSecurityTokenResponseType securityTokenResponse = securityTokenResponseList.get(0); + + // Test the generated token. + Element assertion = null; + for (Object tokenObject : securityTokenResponse.getAny()) { + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + assertion = (Element)rstType.getAny(); + break; + } + } + + assertNotNull(assertion); + String tokenString = DOM2Writer.nodeToString(assertion); + assertTrue(tokenString.contains("AttributeStatement")); + assertTrue(tokenString.contains("ALICE")); //subject changed (to uppercase) + assertTrue(tokenString.contains("doe")); //claim unchanged but requested + assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); + } + + + private Relationship createRelationship() { + Relationship rs = new Relationship(); + ClaimsMapper claimsMapper = new CustomClaimsMapper(); + rs.setClaimsMapper(claimsMapper); + rs.setSourceRealm("A"); + rs.setTargetRealm("B"); + rs.setType(Relationship.FED_TYPE_CLAIMS); + return rs; + } + + + /* + * Create STSPropertiesMBean object + */ + private STSPropertiesMBean createSTSPropertiesMBean(Crypto crypto) throws WSSecurityException { + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + return stsProperties; + } + + + /* + * Create a security context object + */ + private SecurityContext createSecurityContext(final Principal p) { + return new SecurityContext() { + public Principal getUserPrincipal() { + return p; + } + public boolean isUserInRole(String role) { + return false; + } + }; + } + + /* + * Mock up an AppliesTo element using the supplied address + */ + private Element createAppliesToElement(String addressUrl) { + Document doc = DOMUtils.createDocument(); + Element appliesTo = doc.createElementNS(STSConstants.WSP_NS, "wsp:AppliesTo"); + appliesTo.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsp", STSConstants.WSP_NS); + Element endpointRef = doc.createElementNS(STSConstants.WSA_NS_05, "wsa:EndpointReference"); + endpointRef.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsa", STSConstants.WSA_NS_05); + Element address = doc.createElementNS(STSConstants.WSA_NS_05, "wsa:Address"); + address.setAttributeNS(WSConstants.XMLNS_NS, "xmlns:wsa", STSConstants.WSA_NS_05); + address.setTextContent(addressUrl); + endpointRef.appendChild(address); + appliesTo.appendChild(endpointRef); + return appliesTo; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + /* + * Mock up a SecondaryParameters DOM Element containing some claims + */ + private Element createSecondaryParameters() { + Document doc = DOMUtils.createDocument(); + Element secondary = doc.createElementNS(STSConstants.WST_NS_05_12, "SecondaryParameters"); + secondary.setAttributeNS(WSConstants.XMLNS_NS, "xmlns", STSConstants.WST_NS_05_12); + + Element claims = doc.createElementNS(STSConstants.WST_NS_05_12, "Claims"); + claims.setAttributeNS(null, "Dialect", STSConstants.IDT_NS_05_05); + + Element claimType = createClaimsType(doc); + claims.appendChild(claimType); + Element claimValue = createClaimValue(doc); + claims.appendChild(claimValue); + secondary.appendChild(claims); + + return secondary; + } + + private Element createClaimsType(Document doc) { + Element claimType = doc.createElementNS(STSConstants.IDT_NS_05_05, "ClaimType"); + claimType.setAttributeNS( + null, "Uri", ClaimTypes.LASTNAME.toString() + ); + claimType.setAttributeNS(WSConstants.XMLNS_NS, "xmlns", STSConstants.IDT_NS_05_05); + + return claimType; + } + + private Element createClaimValue(Document doc) { + Element claimValue = doc.createElementNS(STSConstants.IDT_NS_05_05, "ClaimValue"); + claimValue.setAttributeNS(null, "Uri", ROLE_CLAIM.toString()); + claimValue.setAttributeNS(WSConstants.XMLNS_NS, "xmlns", STSConstants.IDT_NS_05_05); + Element value = doc.createElementNS(STSConstants.IDT_NS_05_05, "Value"); + value.setTextContent("administrator"); + claimValue.appendChild(value); + return claimValue; + } + + private Map createSamlRealms() { + // Create Realms + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); + samlRealm.setIssuer("A-Issuer"); + samlRealms.put("A", samlRealm); + samlRealm = new RealmProperties(); + samlRealm.setIssuer("B-Issuer"); + samlRealms.put("B", samlRealm); + return samlRealms; + } + + /* + * Mock up an SAML assertion element + */ + private Element createSAMLAssertion( + String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler, + Map realms + ) throws WSSecurityException { + + SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); + samlTokenProvider.setRealmMap(realms); + List customProviderList = + new ArrayList(); + customProviderList.add(new ClaimsAttributeStatementProvider()); + samlTokenProvider.setAttributeStatementProviders(customProviderList); + + TokenProviderParameters providerParameters = + createProviderParameters( + tokenType, STSConstants.BEARER_KEY_KEYTYPE, crypto, signatureUsername, callbackHandler + ); + if (realms != null) { + providerParameters.setRealm("A"); + } + + // Set the ClaimsManager + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + providerParameters.setClaimsManager(claimsManager); + + ClaimCollection requestedClaims = new ClaimCollection(); + Claim requestClaim = new Claim(); + requestClaim.setClaimType(ClaimTypes.LASTNAME); + requestClaim.setOptional(false); + requestedClaims.add(requestClaim); + providerParameters.setRequestedSecondaryClaims(requestedClaims); + + TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return (Element)providerResponse.getToken(); + } + + private TokenProviderParameters createProviderParameters( + String tokenType, String keyType, Crypto crypto, + String signatureUsername, CallbackHandler callbackHandler + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + keyRequirements.setKeyType(keyType); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername(signatureUsername); + stsProperties.setCallbackHandler(callbackHandler); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTClaimsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTClaimsTest.java new file mode 100644 index 00000000000..b735f3aeca5 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTClaimsTest.java @@ -0,0 +1,288 @@ +/** + * 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.sts.token.provider; + +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rt.security.claims.Claim; +import org.apache.cxf.rt.security.claims.ClaimCollection; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.claims.ClaimTypes; +import org.apache.cxf.sts.claims.ClaimsHandler; +import org.apache.cxf.sts.claims.ClaimsManager; +import org.apache.cxf.sts.claims.StaticClaimsHandler; +import org.apache.cxf.sts.common.CustomClaimsHandler; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; + +/** + * A unit test for creating JWT Tokens with various claims populated by a ClaimsHandler. + */ +public class JWTClaimsTest extends org.junit.Assert { + + public static final URI CLAIM_STATIC_COMPANY = + URI.create("http://apache.org/claims/test/company"); + + public static final URI CLAIM_APPLICATION = + URI.create("http://apache.org/claims/test/applicationId"); + + private static final String CLAIM_STATIC_COMPANY_VALUE = "myc@mpany"; + + private static final String APPLICATION_APPLIES_TO = "http://dummy-service.com/dummy"; + + /** + * Test the creation of a JWTToken with various claims set by a ClaimsHandler. + */ + @org.junit.Test + public void testJWTClaims() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE, null); + + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + providerParameters.setClaimsManager(claimsManager); + + ClaimCollection claims = createClaims(); + providerParameters.setRequestedPrimaryClaims(claims); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(ClaimTypes.EMAILADDRESS.toString()), "alice@cxf.apache.org"); + assertEquals(jwt.getClaim(ClaimTypes.FIRSTNAME.toString()), "alice"); + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); + } + + /** + * Test the creation of a JWTToken with various claims set by a ClaimsHandler. + * We have both a primary claim (sent in wst:RequestSecurityToken) and a secondary claim + * (send in wst:RequestSecurityToken/wst:SecondaryParameters). + */ + @org.junit.Test + public void testJWTMultipleClaims() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE, null); + + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + providerParameters.setClaimsManager(claimsManager); + + ClaimCollection primaryClaims = createClaims(); + providerParameters.setRequestedPrimaryClaims(primaryClaims); + + ClaimCollection secondaryClaims = new ClaimCollection(); + Claim claim = new Claim(); + claim.setClaimType(ClaimTypes.STREETADDRESS); + secondaryClaims.add(claim); + providerParameters.setRequestedSecondaryClaims(secondaryClaims); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(ClaimTypes.EMAILADDRESS.toString()), "alice@cxf.apache.org"); + assertEquals(jwt.getClaim(ClaimTypes.FIRSTNAME.toString()), "alice"); + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); + assertEquals(jwt.getClaim(ClaimTypes.STREETADDRESS.toString()), "1234 1st Street"); + } + + /** + * Test the creation of a JWTToken with various claims set by a ClaimsHandler. + * We have both a primary claim (sent in wst:RequestSecurityToken) and a secondary claim + * (send in wst:RequestSecurityToken/wst:SecondaryParameters), and both have the + * same dialect in this test. + */ + @org.junit.Test + public void testJWTMultipleClaimsSameDialect() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE, null); + + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + providerParameters.setClaimsManager(claimsManager); + + ClaimCollection primaryClaims = createClaims(); + primaryClaims.setDialect(ClaimTypes.URI_BASE); + providerParameters.setRequestedPrimaryClaims(primaryClaims); + + ClaimCollection secondaryClaims = new ClaimCollection(); + Claim claim = new Claim(); + claim.setClaimType(ClaimTypes.STREETADDRESS); + secondaryClaims.add(claim); + secondaryClaims.setDialect(ClaimTypes.URI_BASE); + providerParameters.setRequestedSecondaryClaims(secondaryClaims); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(ClaimTypes.EMAILADDRESS.toString()), "alice@cxf.apache.org"); + assertEquals(jwt.getClaim(ClaimTypes.FIRSTNAME.toString()), "alice"); + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); + assertEquals(jwt.getClaim(ClaimTypes.STREETADDRESS.toString()), "1234 1st Street"); + } + + /** + * Test the creation of a JWTToken with StaticClaimsHandler + */ + @org.junit.Test + public void testJWTStaticClaims() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE, null); + + ClaimsManager claimsManager = new ClaimsManager(); + StaticClaimsHandler claimsHandler = new StaticClaimsHandler(); + Map staticClaimsMap = new HashMap(); + staticClaimsMap.put(CLAIM_STATIC_COMPANY.toString(), CLAIM_STATIC_COMPANY_VALUE); + claimsHandler.setGlobalClaims(staticClaimsMap); + claimsManager.setClaimHandlers(Collections.singletonList((ClaimsHandler)claimsHandler)); + providerParameters.setClaimsManager(claimsManager); + + ClaimCollection claims = new ClaimCollection(); + Claim claim = new Claim(); + claim.setClaimType(CLAIM_STATIC_COMPANY); + claims.add(claim); + providerParameters.setRequestedPrimaryClaims(claims); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(CLAIM_STATIC_COMPANY.toString()), CLAIM_STATIC_COMPANY_VALUE); + } + + private TokenProviderParameters createProviderParameters( + String tokenType, String appliesTo + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + if (appliesTo != null) { + parameters.setAppliesToAddress(appliesTo); + } else { + parameters.setAppliesToAddress(APPLICATION_APPLIES_TO); + } + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + /** + * Create a set of parsed Claims + */ + private ClaimCollection createClaims() { + ClaimCollection claims = new ClaimCollection(); + + Claim claim = new Claim(); + claim.setClaimType(ClaimTypes.FIRSTNAME); + claims.add(claim); + + claim = new Claim(); + claim.setClaimType(ClaimTypes.LASTNAME); + claims.add(claim); + + claim = new Claim(); + claim.setClaimType(ClaimTypes.EMAILADDRESS); + claims.add(claim); + + return claims; + } + +} From 8516661b64e630e033425b24b7af53680d1229ca Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 9 Nov 2015 16:42:31 +0000 Subject: [PATCH 0028/1346] Fixing time/expiry stuff for JWT in the STS + tests --- .../provider/DefaultConditionsProvider.java | 43 +- .../token/provider/TokenProviderUtils.java | 82 ++++ .../jwt/DefaultJWTClaimsProvider.java | 209 ++++++++- .../provider/JWTProviderLifetimeTest.java | 409 ++++++++++++++++++ 4 files changed, 696 insertions(+), 47 deletions(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderLifetimeTest.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/DefaultConditionsProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/DefaultConditionsProvider.java index f720ed6d07f..eeb286249c1 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/DefaultConditionsProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/DefaultConditionsProvider.java @@ -25,16 +25,9 @@ import java.util.List; import java.util.logging.Logger; -import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; - -import org.w3c.dom.Element; import org.apache.cxf.common.logging.LogUtils; -import org.apache.cxf.helpers.DOMUtils; -import org.apache.cxf.sts.STSConstants; import org.apache.cxf.sts.request.Lifetime; import org.apache.cxf.sts.request.Participants; -import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.apache.cxf.ws.security.sts.provider.STSException; import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean; import org.apache.wss4j.common.saml.bean.ConditionsBean; @@ -261,41 +254,7 @@ protected List createAudienceRestrictions( * Extract an address from a Participants EPR DOM element */ protected String extractAddressFromParticipantsEPR(Object participants) { - if (participants instanceof Element) { - String localName = ((Element)participants).getLocalName(); - String namespace = ((Element)participants).getNamespaceURI(); - - if (STSConstants.WSA_NS_05.equals(namespace) && "EndpointReference".equals(localName)) { - LOG.fine("Found EndpointReference element"); - Element address = - DOMUtils.getFirstChildWithName((Element)participants, - STSConstants.WSA_NS_05, "Address"); - if (address != null) { - LOG.fine("Found address element"); - return address.getTextContent(); - } - } else if ((STSConstants.WSP_NS.equals(namespace) || STSConstants.WSP_NS_04.equals(namespace)) - && "URI".equals(localName)) { - return ((Element)participants).getTextContent(); - } - LOG.fine("Participants element does not exist or could not be parsed"); - return null; - } else if (participants instanceof JAXBElement) { - JAXBElement jaxbElement = (JAXBElement) participants; - QName participantsName = jaxbElement.getName(); - if (STSConstants.WSA_NS_05.equals(participantsName.getNamespaceURI()) - && "EndpointReference".equals(participantsName.getLocalPart())) { - LOG.fine("Found EndpointReference element"); - EndpointReferenceType endpointReference = (EndpointReferenceType)jaxbElement.getValue(); - if (endpointReference.getAddress() != null) { - LOG.fine("Found address element"); - return endpointReference.getAddress().getValue(); - } - } - LOG.fine("Participants element does not exist or could not be parsed"); - } - - return null; + return TokenProviderUtils.extractAddressFromParticipantsEPR(participants); } } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java new file mode 100644 index 00000000000..406c02e10fd --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java @@ -0,0 +1,82 @@ +/** + * 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.sts.token.provider; + +import java.util.logging.Logger; + +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.w3c.dom.Element; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.ws.addressing.EndpointReferenceType; + +public final class TokenProviderUtils { + + private static final Logger LOG = LogUtils.getL7dLogger(TokenProviderUtils.class); + + private TokenProviderUtils() { + // complete + } + + /** + * Extract an address from a Participants EPR DOM element + */ + public static String extractAddressFromParticipantsEPR(Object participants) { + if (participants instanceof Element) { + String localName = ((Element)participants).getLocalName(); + String namespace = ((Element)participants).getNamespaceURI(); + + if (STSConstants.WSA_NS_05.equals(namespace) && "EndpointReference".equals(localName)) { + LOG.fine("Found EndpointReference element"); + Element address = + DOMUtils.getFirstChildWithName((Element)participants, + STSConstants.WSA_NS_05, "Address"); + if (address != null) { + LOG.fine("Found address element"); + return address.getTextContent(); + } + } else if ((STSConstants.WSP_NS.equals(namespace) || STSConstants.WSP_NS_04.equals(namespace)) + && "URI".equals(localName)) { + return ((Element)participants).getTextContent(); + } + LOG.fine("Participants element does not exist or could not be parsed"); + return null; + } else if (participants instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) participants; + QName participantsName = jaxbElement.getName(); + if (STSConstants.WSA_NS_05.equals(participantsName.getNamespaceURI()) + && "EndpointReference".equals(participantsName.getLocalPart())) { + LOG.fine("Found EndpointReference element"); + EndpointReferenceType endpointReference = (EndpointReferenceType)jaxbElement.getValue(); + if (endpointReference.getAddress() != null) { + LOG.fine("Found address element"); + return endpointReference.getAddress().getValue(); + } + } + LOG.fine("Participants element does not exist or could not be parsed"); + } + + return null; + } + +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java index 1e80f960ab3..dcdc7c8612f 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/DefaultJWTClaimsProvider.java @@ -19,8 +19,11 @@ package org.apache.cxf.sts.token.provider.jwt; import java.security.Principal; +import java.text.ParseException; +import java.util.ArrayList; import java.util.Date; import java.util.Iterator; +import java.util.List; import java.util.UUID; import java.util.logging.Logger; @@ -28,14 +31,19 @@ import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.claims.ClaimsUtils; import org.apache.cxf.sts.claims.ProcessedClaim; import org.apache.cxf.sts.claims.ProcessedClaimCollection; +import org.apache.cxf.sts.request.Lifetime; +import org.apache.cxf.sts.request.Participants; import org.apache.cxf.sts.request.ReceivedToken; import org.apache.cxf.sts.request.ReceivedToken.STATE; import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderUtils; import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.wss4j.dom.util.XmlSchemaDateFormat; /** * A default implementation to create a JWTClaims object. The Subject name is the name @@ -43,8 +51,16 @@ */ public class DefaultJWTClaimsProvider implements JWTClaimsProvider { + public static final long DEFAULT_MAX_LIFETIME = 60L * 60L * 12L; + private static final Logger LOG = LogUtils.getL7dLogger(DefaultJWTClaimsProvider.class); private boolean useX500CN; + + private long lifetime = 60L * 30L; + private long maxLifetime = DEFAULT_MAX_LIFETIME; + private boolean failLifetimeExceedance = true; + private boolean acceptClientLifetime; + private long futureTimeToLive = 60L; /** * Get a JwtClaims object. @@ -66,11 +82,9 @@ public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParam handleWSTrustClaims(jwtClaimsProviderParameters, claims); - Date currentDate = new Date(); - claims.setIssuedAt(currentDate.getTime() / 1000L); - long currentTime = currentDate.getTime() + 300L * 1000L; - currentDate.setTime(currentTime); - claims.setExpiryTime(currentDate.getTime() / 1000L); + handleConditions(jwtClaimsProviderParameters, claims); + + handleAudienceRestriction(jwtClaimsProviderParameters, claims); return claims; } @@ -155,6 +169,109 @@ protected void handleWSTrustClaims(JWTClaimsProviderParameters jwtClaimsProvider } } + protected void handleConditions(JWTClaimsProviderParameters jwtClaimsProviderParameters, JwtClaims claims) { + TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters(); + + Date currentDate = new Date(); + long currentTimeInSeconds = currentDate.getTime() / 1000L; + + // Set the defaults first + claims.setIssuedAt(currentTimeInSeconds); + claims.setNotBefore(currentTimeInSeconds); + claims.setExpiryTime(currentTimeInSeconds + lifetime); + + Lifetime tokenLifetime = providerParameters.getTokenRequirements().getLifetime(); + if (lifetime > 0 && acceptClientLifetime && tokenLifetime != null + && tokenLifetime.getCreated() != null && tokenLifetime.getExpires() != null) { + try { + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + Date creationTime = fmt.parse(tokenLifetime.getCreated()); + Date expirationTime = fmt.parse(tokenLifetime.getExpires()); + if (creationTime == null || expirationTime == null) { + LOG.fine("Error in parsing Timestamp Created or Expiration Strings"); + throw new STSException( + "Error in parsing Timestamp Created or Expiration Strings", + STSException.INVALID_TIME + ); + } + + // Check to see if the created time is in the future + Date validCreation = new Date(); + long currentTime = validCreation.getTime(); + if (futureTimeToLive > 0) { + validCreation.setTime(currentTime + futureTimeToLive * 1000L); + } + if (creationTime.after(validCreation)) { + LOG.fine("The Created Time is too far in the future"); + throw new STSException("The Created Time is too far in the future", STSException.INVALID_TIME); + } + + long requestedLifetime = expirationTime.getTime() - creationTime.getTime(); + if (requestedLifetime > (getMaxLifetime() * 1000L)) { + StringBuilder sb = new StringBuilder(); + sb.append("Requested lifetime [").append(requestedLifetime / 1000L); + sb.append(" sec] exceed configured maximum lifetime [").append(getMaxLifetime()); + sb.append(" sec]"); + LOG.warning(sb.toString()); + if (isFailLifetimeExceedance()) { + throw new STSException("Requested lifetime exceeds maximum lifetime", + STSException.INVALID_TIME); + } else { + expirationTime.setTime(creationTime.getTime() + (getMaxLifetime() * 1000L)); + } + } + + long creationTimeInSeconds = creationTime.getTime() / 1000L; + claims.setIssuedAt(creationTimeInSeconds); + claims.setNotBefore(creationTimeInSeconds); + claims.setExpiryTime(expirationTime.getTime() / 1000L); + } catch (ParseException e) { + LOG.warning("Failed to parse life time element: " + e.getMessage()); + } + } + } + + /** + * Set the audience restriction claim. The Audiences are from an AppliesTo address, and the wst:Participants + * (if either exist). + */ + protected void handleAudienceRestriction( + JWTClaimsProviderParameters jwtClaimsProviderParameters, JwtClaims claims + ) { + TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters(); + + List audiences = new ArrayList(); + String appliesToAddress = providerParameters.getAppliesToAddress(); + if (appliesToAddress != null) { + audiences.add(appliesToAddress); + } + + Participants participants = providerParameters.getTokenRequirements().getParticipants(); + if (participants != null) { + String address = TokenProviderUtils.extractAddressFromParticipantsEPR(participants.getPrimaryParticipant()); + if (address != null) { + audiences.add(address); + } + + if (participants.getParticipants() != null) { + for (Object participant : participants.getParticipants()) { + if (participant != null) { + address = TokenProviderUtils.extractAddressFromParticipantsEPR(participant); + if (address != null) { + audiences.add(address); + } + } + } + } + } + + if (audiences.size() == 1) { + claims.setAudience(audiences.get(0)); + } else if (!audiences.isEmpty()) { + claims.setProperty(JwtConstants.CLAIM_AUDIENCE, audiences); + } + } + public boolean isUseX500CN() { return useX500CN; } @@ -162,4 +279,86 @@ public boolean isUseX500CN() { public void setUseX500CN(boolean useX500CN) { this.useX500CN = useX500CN; } + + /** + * Get how long (in seconds) a client-supplied Created Element is allowed to be in the future. + * The default is 60 seconds to avoid common problems relating to clock skew. + */ + public long getFutureTimeToLive() { + return futureTimeToLive; + } + + /** + * Set how long (in seconds) a client-supplied Created Element is allowed to be in the future. + * The default is 60 seconds to avoid common problems relating to clock skew. + */ + public void setFutureTimeToLive(long futureTimeToLive) { + this.futureTimeToLive = futureTimeToLive; + } + + /** + * Set the default lifetime in seconds for issued JWT tokens + * @param default lifetime in seconds + */ + public void setLifetime(long lifetime) { + this.lifetime = lifetime; + } + + /** + * Get the default lifetime in seconds for issued JWT token where requestor + * doesn't specify a lifetime element + * @return the lifetime in seconds + */ + public long getLifetime() { + return lifetime; + } + + /** + * Set the maximum lifetime in seconds for issued JWT tokens + * @param maximum lifetime in seconds + */ + public void setMaxLifetime(long maxLifetime) { + this.maxLifetime = maxLifetime; + } + + /** + * Get the maximum lifetime in seconds for issued JWT token + * if requestor specifies lifetime element + * @return the maximum lifetime in seconds + */ + public long getMaxLifetime() { + return maxLifetime; + } + + /** + * Is client lifetime element accepted + * Default: false + */ + public boolean isAcceptClientLifetime() { + return this.acceptClientLifetime; + } + + /** + * Set whether client lifetime is accepted + */ + public void setAcceptClientLifetime(boolean acceptClientLifetime) { + this.acceptClientLifetime = acceptClientLifetime; + } + + /** + * If requested lifetime exceeds shall it fail (default) + * or overwrite with maximum lifetime + */ + public boolean isFailLifetimeExceedance() { + return this.failLifetimeExceedance; + } + + /** + * If requested lifetime exceeds shall it fail (default) + * or overwrite with maximum lifetime + */ + public void setFailLifetimeExceedance(boolean failLifetimeExceedance) { + this.failLifetimeExceedance = failLifetimeExceedance; + } + } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderLifetimeTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderLifetimeTest.java new file mode 100644 index 00000000000..3d16d40b791 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderLifetimeTest.java @@ -0,0 +1,409 @@ +/** + * 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.sts.token.provider; + +import java.util.Date; +import java.util.Properties; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.Lifetime; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.jwt.DefaultJWTClaimsProvider; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.dom.util.XmlSchemaDateFormat; + + +/** + * Some unit tests for creating JWT Tokens with lifetime + */ +public class JWTProviderLifetimeTest extends org.junit.Assert { + + /** + * Issue JWT token with a valid requested lifetime + */ + @org.junit.Test + public void testJWTValidLifetime() throws Exception { + + int requestedLifetime = 60; + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to 1 minute + Date creationTime = new Date(); + Date expirationTime = new Date(); + expirationTime.setTime(creationTime.getTime() + (requestedLifetime * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + lifetime.setExpires(fmt.format(expirationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + assertEquals(requestedLifetime * 1000L, providerResponse.getExpires().getTime() + - providerResponse.getCreated().getTime()); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT), providerResponse.getCreated().getTime() / 1000L); + } + + /** + * Issue JWT token with a lifetime configured in JWTTokenProvider + * No specific lifetime requested + */ + @org.junit.Test + public void testJWTProviderLifetime() throws Exception { + + long providerLifetime = 10 * 600L; + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setLifetime(providerLifetime); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + assertEquals(providerLifetime * 1000L, providerResponse.getExpires().getTime() + - providerResponse.getCreated().getTime()); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT), providerResponse.getCreated().getTime() / 1000L); + } + + + /** + * Issue JWT token with a with a lifetime + * which exceeds configured maximum lifetime + */ + @org.junit.Test + public void testJWTExceededConfiguredMaxLifetime() throws Exception { + + long maxLifetime = 30 * 60L; // 30 minutes + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setMaxLifetime(maxLifetime); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to 35 minutes + long requestedLifetime = 35 * 60L; + Date creationTime = new Date(); + Date expirationTime = new Date(); + expirationTime.setTime(creationTime.getTime() + (requestedLifetime * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + lifetime.setExpires(fmt.format(expirationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + try { + tokenProvider.createToken(providerParameters); + fail("Failure expected due to exceeded lifetime"); + } catch (STSException ex) { + //expected + } + } + + /** + * Issue JWT token with a with a lifetime + * which exceeds default maximum lifetime + */ + @org.junit.Test + public void testJWTExceededDefaultMaxLifetime() throws Exception { + + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to Default max lifetime plus 1 + long requestedLifetime = DefaultConditionsProvider.DEFAULT_MAX_LIFETIME + 1; + Date creationTime = new Date(); + Date expirationTime = new Date(); + expirationTime.setTime(creationTime.getTime() + (requestedLifetime * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + lifetime.setExpires(fmt.format(expirationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + try { + tokenProvider.createToken(providerParameters); + fail("Failure expected due to exceeded lifetime"); + } catch (STSException ex) { + //expected + } + } + + /** + * Issue JWT token with a with a lifetime + * which exceeds configured maximum lifetime + * Lifetime reduced to maximum lifetime + */ + @org.junit.Test + public void testJWTExceededConfiguredMaxLifetimeButUpdated() throws Exception { + + long maxLifetime = 30 * 60L; // 30 minutes + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setMaxLifetime(maxLifetime); + claimsProvider.setFailLifetimeExceedance(false); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to 35 minutes + long requestedLifetime = 35 * 60L; + Date creationTime = new Date(); + Date expirationTime = new Date(); + expirationTime.setTime(creationTime.getTime() + (requestedLifetime * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + lifetime.setExpires(fmt.format(expirationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + assertEquals(maxLifetime * 1000L, providerResponse.getExpires().getTime() + - providerResponse.getCreated().getTime()); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT), providerResponse.getCreated().getTime() / 1000L); + } + + /** + * Issue JWT token with a near future Created Lifetime. This should pass as we allow a future + * dated Lifetime up to 60 seconds to avoid clock skew problems. + */ + @org.junit.Test + public void testJWTNearFutureCreatedLifetime() throws Exception { + + int requestedLifetime = 60; + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to 1 minute + Date creationTime = new Date(); + Date expirationTime = new Date(); + expirationTime.setTime(creationTime.getTime() + (requestedLifetime * 1000L)); + creationTime.setTime(creationTime.getTime() + (10 * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + lifetime.setExpires(fmt.format(expirationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + assertEquals(50L * 1000L, providerResponse.getExpires().getTime() + - providerResponse.getCreated().getTime()); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT), providerResponse.getCreated().getTime() / 1000L); + } + + /** + * Issue JWT token with a future Created Lifetime. This should fail as we only allow a future + * dated Lifetime up to 60 seconds to avoid clock skew problems. + */ + @org.junit.Test + public void testJWTFarFutureCreatedLifetime() throws Exception { + + int requestedLifetime = 60; + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to 1 minute + Date creationTime = new Date(); + creationTime.setTime(creationTime.getTime() + (60L * 2L * 1000L)); + Date expirationTime = new Date(); + expirationTime.setTime(creationTime.getTime() + (requestedLifetime * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + lifetime.setExpires(fmt.format(expirationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + try { + tokenProvider.createToken(providerParameters); + fail("Failure expected on a Created Element too far in the future"); + } catch (STSException ex) { + // expected + } + + // Now allow this sort of Created Element + claimsProvider.setFutureTimeToLive(60L * 60L); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT), providerResponse.getCreated().getTime() / 1000L); + } + + /** + * Issue JWT token with no Expires element. This will be rejected, but will default to the + * configured TTL and so the request will pass. + */ + @org.junit.Test + public void testJWTNoExpires() throws Exception { + + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + DefaultJWTClaimsProvider claimsProvider = new DefaultJWTClaimsProvider(); + claimsProvider.setAcceptClientLifetime(true); + tokenProvider.setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + // Set expected lifetime to 1 minute + Date creationTime = new Date(); + creationTime.setTime(creationTime.getTime() + (60L * 2L * 1000L)); + Lifetime lifetime = new Lifetime(); + XmlSchemaDateFormat fmt = new XmlSchemaDateFormat(); + lifetime.setCreated(fmt.format(creationTime)); + providerParameters.getTokenRequirements().setLifetime(lifetime); + + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + assertEquals(claimsProvider.getLifetime() * 1000L, providerResponse.getExpires().getTime() + - providerResponse.getCreated().getTime()); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + assertEquals(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT), providerResponse.getCreated().getTime() / 1000L); + } + + private TokenProviderParameters createProviderParameters(String tokenType) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + + +} From b6018a47e19bf98ad405baf51e363eae9972f4dd Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 9 Nov 2015 21:53:08 +0000 Subject: [PATCH 0029/1346] One more update to oauth code filter --- .../cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 49df2d67028..ae9f942bc8e 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -114,7 +114,7 @@ private void checkSecurityContextEnd(ContainerRequestContext rc, if (sc == null || sc.getUserPrincipal() == null) { if (codeParam == null && requestParams.containsKey(OAuthConstants.ERROR_KEY) - && OAuthConstants.ACCESS_DENIED.equals(requestParams.get(OAuthConstants.ERROR_KEY)) + && OAuthConstants.ACCESS_DENIED.equals(requestParams.getFirst(OAuthConstants.ERROR_KEY)) && !faultAccessDeniedResponses) { if (!applicationCanHandleAccessDenied) { rc.abortWith(Response.ok(new AccessDeniedResponse()).build()); From 2814cce0def903ab0e2d2526915ab20253a947ee Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 10 Nov 2015 12:11:31 +0000 Subject: [PATCH 0030/1346] [CXF-6663] Prototyping a scope based access control code --- .../oauth2/filters/ConfidentialClient.java | 39 +++++ .../oauth2/filters/OAuthScopesFilter.java | 150 ++++++++++++++++++ .../rs/security/oauth2/filters/Scopes.java | 44 +++++ 3 files changed, 233 insertions(+) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/ConfidentialClient.java create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthScopesFilter.java create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/Scopes.java diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/ConfidentialClient.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/ConfidentialClient.java new file mode 100644 index 00000000000..fcd784aec58 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/ConfidentialClient.java @@ -0,0 +1,39 @@ +/** + * 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.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +/** + * Indicates that only OAuth2 confidential clients can invoke a given resource method. + * Confidential clients are less likely to have their access tokens lost or stolen due + * to them being generally more secure than public clients which can not keep their client + * secrets. + */ +public @interface ConfidentialClient { +} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthScopesFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthScopesFilter.java new file mode 100644 index 00000000000..f26cf785535 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthScopesFilter.java @@ -0,0 +1,150 @@ +/** + * 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.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Context; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.ClassHelper; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.utils.ExceptionUtils; +import org.apache.cxf.rs.security.oauth2.common.OAuthContext; +import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; +import org.apache.cxf.rs.security.oauth2.utils.OAuthContextUtils; + + +public class OAuthScopesFilter implements ContainerRequestFilter { + + private static final Logger LOG = LogUtils.getL7dLogger(OAuthScopesFilter.class); + private static final Set SKIP_METHODS; + static { + SKIP_METHODS = new HashSet(); + SKIP_METHODS.addAll(Arrays.asList( + new String[] {"wait", "notify", "notifyAll", + "equals", "toString", "hashCode"})); + } + + @Context + private MessageContext mc; + private Map> scopesMap = new HashMap>(); + private Map scopesMatchAllMap = new HashMap(); + private Set confidentialClientMethods = new HashSet(); + + public void setSecuredObject(Object object) { + Class cls = ClassHelper.getRealClass(object); + checkSecureClass(cls); + if (scopesMap.isEmpty()) { + LOG.warning("The scopes map is empty"); + } else if (LOG.isLoggable(Level.FINE)) { + for (Map.Entry> entry : scopesMap.entrySet()) { + LOG.fine("Method: " + entry.getKey() + ", scopes: " + entry.getValue()); + } + } + } + + protected void checkSecureClass(Class cls) { + if (cls == null || cls == Object.class) { + return; + } + Scopes classScopes = cls.getAnnotation(Scopes.class); + ConfidentialClient classConfClient = cls.getAnnotation(ConfidentialClient.class); + for (Method m : cls.getMethods()) { + if (SKIP_METHODS.contains(m.getName())) { + continue; + } + Scopes methodScopes = m.getAnnotation(Scopes.class); + Scopes theScopes = methodScopes == null ? classScopes : methodScopes; + if (theScopes != null) { + scopesMap.put(m.getName(), Arrays.asList(theScopes.value())); + scopesMatchAllMap.put(m.getName(), theScopes.matchAll()); + } + + ConfidentialClient mConfClient = m.getAnnotation(ConfidentialClient.class); + if (classConfClient != null || mConfClient != null) { + confidentialClientMethods.add(m.getName()); + } + } + checkSecureClass(cls.getSuperclass()); + for (Class interfaceCls : cls.getInterfaces()) { + checkSecureClass(interfaceCls); + } + } + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + Method m = getTargetMethod(); + checkClient(m); + checkScopes(m); + } + protected void checkClient(Method m) { + if (confidentialClientMethods.contains(m.getName())) { + OAuthContext context = OAuthContextUtils.getContext(mc); + if (!context.isClientConfidential()) { + LOG.warning("Non confidential client " + context.getClientId() + + " has attempted to invoke " + m.getName()); + throw ExceptionUtils.toForbiddenException(null, null); + } + } + } + protected void checkScopes(Method m) { + List methodScopes = scopesMap.get(m.getName()); + if (methodScopes == null) { + return; + } + boolean matchAll = scopesMatchAllMap.get(m.getName()); + OAuthContext context = OAuthContextUtils.getContext(mc); + List requestScopes = new LinkedList(); + for (OAuthPermission perm : context.getPermissions()) { + if (matchAll) { + requestScopes.add(perm.getPermission()); + } else if (methodScopes.contains(perm.getPermission())) { + return; + } + } + + if (!requestScopes.containsAll(methodScopes)) { + LOG.warning("Scopes do not match"); + throw ExceptionUtils.toForbiddenException(null, null); + } + + } + protected Method getTargetMethod() { + Method method = (Method)mc.get("org.apache.cxf.resource.method"); + if (method != null) { + return method; + } + throw ExceptionUtils.toForbiddenException(null, null); + } + + +} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/Scopes.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/Scopes.java new file mode 100644 index 00000000000..8e3eb48007d --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/Scopes.java @@ -0,0 +1,44 @@ +/** + * 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.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +/** + * Specifies a list of scopes that a given access token should have in order for + * the client invocation to succeed. + */ +public @interface Scopes { + + String[] value(); + + /** + * If set to true then all the values of this scope have to be matched + */ + boolean matchAll() default false; +} From 243384197f8c395ae001ae0c3ea87aa9724dcda0 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 10 Nov 2015 11:19:49 +0000 Subject: [PATCH 0031/1346] Store JWT tokens in the cache --- .../token/provider/jwt/JWTTokenProvider.java | 44 ++++++++----------- .../token/provider/JWTTokenProviderTest.java | 34 ++++++++++++++ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java index 0f5a3830587..5afffda1a51 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -42,12 +42,14 @@ import org.apache.cxf.rs.security.jose.jwt.JwtToken; import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.SignatureProperties; +import org.apache.cxf.sts.cache.CacheUtils; import org.apache.cxf.sts.request.TokenRequirements; import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; import org.apache.cxf.sts.token.realm.RealmProperties; import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.Merlin; import org.apache.wss4j.common.ext.WSPasswordCallback; @@ -109,31 +111,6 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters JwtClaims claims = jwtClaimsProvider.getJwtClaims(jwtClaimsProviderParameters); try { - /* - Document doc = DOMUtils.createDocument(); - SamlAssertionWrapper assertion = createSamlToken(tokenParameters, secret, doc); - Element token = assertion.toDOM(doc); - - // set the token in cache (only if the token is signed) - byte[] signatureValue = assertion.getSignatureValue(); - if (tokenParameters.getTokenStore() != null && signatureValue != null - && signatureValue.length > 0) { - DateTime validTill = null; - if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)) { - validTill = assertion.getSaml2().getConditions().getNotOnOrAfter(); - } else { - validTill = assertion.getSaml1().getConditions().getNotOnOrAfter(); - } - - SecurityToken securityToken = - CacheUtils.createSecurityTokenForStorage(token, assertion.getId(), - validTill.toDate(), tokenParameters.getPrincipal(), tokenParameters.getRealm(), - tokenParameters.getTokenRequirements().getRenewing()); - CacheUtils.storeTokenInCache( - securityToken, tokenParameters.getTokenStore(), signatureValue); - } - */ - JwtToken token = new JwtToken(claims); String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties(), @@ -147,8 +124,23 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters if (claims.getIssuedAt() > 0) { response.setCreated(new Date(claims.getIssuedAt() * 1000L)); } + Date expires = null; if (claims.getExpiryTime() > 0) { - response.setExpires(new Date(claims.getExpiryTime() * 1000L)); + expires = new Date(claims.getExpiryTime() * 1000L); + response.setExpires(expires); + } + + // set the token in cache (only if the token is signed) + if (signToken && tokenParameters.getTokenStore() != null) { + SecurityToken securityToken = + CacheUtils.createSecurityTokenForStorage(null, claims.getTokenId(), + expires, tokenParameters.getPrincipal(), tokenParameters.getRealm(), + tokenParameters.getTokenRequirements().getRenewing()); + securityToken.setData(tokenData.getBytes()); + + String signature = tokenData.substring(tokenData.lastIndexOf(".") + 1); + CacheUtils.storeTokenInCache( + securityToken, tokenParameters.getTokenStore(), signature.getBytes()); } LOG.fine("JWT Token successfully created"); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java index aed28ef0966..6273e0e6f2f 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.sts.token.provider; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Properties; import org.apache.cxf.jaxws.context.WebServiceContextImpl; @@ -35,6 +36,7 @@ import org.apache.cxf.sts.request.TokenRequirements; import org.apache.cxf.sts.service.EncryptionProperties; import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.cxf.ws.security.tokenstore.TokenStore; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; @@ -113,6 +115,38 @@ public void testCreateSignedJWT() throws Exception { assertTrue(jwtConsumer.verifySignatureWith(certs[0], SignatureAlgorithm.RS256)); } + @org.junit.Test + public void testCachedSignedJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + + // Check that the token is stored correctly in the cache + String signature = token.substring(token.lastIndexOf(".") + 1); + SecurityToken secToken = tokenStore.getToken(Integer.toString(Arrays.hashCode(signature.getBytes()))); + Assert.assertNotNull(secToken); + } + private TokenProviderParameters createProviderParameters() throws WSSecurityException { TokenProviderParameters parameters = new TokenProviderParameters(); From 362437e222ea79e92078323d865fb0b2d7195610 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 10 Nov 2015 12:12:17 +0000 Subject: [PATCH 0032/1346] Adding some OnBehalfOf/ActAs tests for JWT tokens --- .../sts/operation/IssueJWTClaimsUnitTest.java | 108 ++-- .../operation/IssueJWTOnbehalfofUnitTest.java | 507 ++++++++++++++++++ .../token/provider/JWTProviderActAsTest.java | 196 +++++++ .../provider/JWTProviderOnBehalfOfTest.java | 196 +++++++ 4 files changed, 947 insertions(+), 60 deletions(-) create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderActAsTest.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderOnBehalfOfTest.java diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java index 5e9219141cb..ab20e9647d8 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java @@ -77,7 +77,6 @@ import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; -import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.ext.WSSecurityException; @@ -283,14 +282,12 @@ public void testIssueJaxbJWTToken() throws Exception { } /** - * Test to successfully issue a SAML 2 token (realm "B") on-behalf-of a SAML 2 token + * Test to successfully issue a JWT token (realm "B") on-behalf-of a SAML 2 token * which was issued by realm "A". * The relationship type between realm A and B is: FederateClaims - * TODO */ @org.junit.Test - @org.junit.Ignore - public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() + public void testIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateClaims() throws Exception { TokenIssueOperation issueOperation = new TokenIssueOperation(); @@ -298,13 +295,9 @@ public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() // Add Token Provider List providerList = new ArrayList(); - SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); - samlTokenProvider.setRealmMap(realms); - List customProviderList = - new ArrayList(); - customProviderList.add(new ClaimsAttributeStatementProvider()); - samlTokenProvider.setAttributeStatementProviders(customProviderList); - providerList.add(samlTokenProvider); + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + tokenProvider.setRealmMap(realms); + providerList.add(tokenProvider); issueOperation.setTokenProviders(providerList); TokenDelegationHandler delegationHandler = new SAMLDelegationHandler(); @@ -342,7 +335,7 @@ public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() RequestSecurityTokenType request = new RequestSecurityTokenType(); JAXBElement tokenType = new JAXBElement( - QNameConstants.TOKEN_TYPE, String.class, WSConstants.WSS_SAML2_TOKEN_TYPE + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE ); request.getAny().add(tokenType); @@ -392,57 +385,55 @@ public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateClaims() List securityTokenResponseList = issueToken(issueOperation, request, webServiceContext); - RequestSecurityTokenResponseType securityTokenResponse = securityTokenResponseList.get(0); // Test the generated token. - Element assertion = null; - for (Object tokenObject : securityTokenResponse.getAny()) { - if (tokenObject instanceof JAXBElement - && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { - RequestedSecurityTokenType rstType = - (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); - assertion = (Element)rstType.getAny(); + String jwtToken = null; + for (Object tokenObject : securityTokenResponseList.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); break; } } - assertNotNull(assertion); - String tokenString = DOM2Writer.nodeToString(assertion); - assertTrue(tokenString.contains("AttributeStatement")); - assertTrue(tokenString.contains("alice")); //subject unchanged - assertTrue(tokenString.contains("DOE")); //transformed claim (to uppercase) - assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + // subject unchanged + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + // transformed claim (to uppercase) + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "DOE"); } /** - * Test to successfully issue a SAML 2 token (realm "B") on-behalf-of a SAML 2 token + * Test to successfully issue a JWT token (realm "B") on-behalf-of a SAML 2 token * which was issued by realm "A". * The relationship type between realm A and B is: FederateIdentity * IdentityMapper is configured globally in STSPropertiesMBean - * TODO */ @org.junit.Test - @org.junit.Ignore - public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentityGlobalConfig() + public void testIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateIdentityGlobalConfig() throws Exception { - runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity(true); + runIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateIdentity(true); } /** - * Test to successfully issue a SAML 2 token (realm "B") on-behalf-of a SAML 2 token + * Test to successfully issue a JWT token (realm "B") on-behalf-of a SAML 2 token * which was issued by realm "A". * The relationship type between realm A and B is: FederateIdentity * IdentityMapper is configured in the Relationship - * TODO */ @org.junit.Test - @org.junit.Ignore - public void testIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentityRelationshipConfig() + public void testIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateIdentityRelationshipConfig() throws Exception { - runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity(false); + runIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateIdentity(false); } - private void runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( + private void runIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateIdentity( boolean useGlobalIdentityMapper) throws WSSecurityException { TokenIssueOperation issueOperation = new TokenIssueOperation(); @@ -450,13 +441,9 @@ private void runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( // Add Token Provider List providerList = new ArrayList(); - SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); - samlTokenProvider.setRealmMap(realms); - List customProviderList = - new ArrayList(); - customProviderList.add(new ClaimsAttributeStatementProvider()); - samlTokenProvider.setAttributeStatementProviders(customProviderList); - providerList.add(samlTokenProvider); + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + tokenProvider.setRealmMap(realms); + providerList.add(tokenProvider); issueOperation.setTokenProviders(providerList); TokenDelegationHandler delegationHandler = new SAMLDelegationHandler(); @@ -500,7 +487,7 @@ private void runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( RequestSecurityTokenType request = new RequestSecurityTokenType(); JAXBElement tokenType = new JAXBElement( - QNameConstants.TOKEN_TYPE, String.class, WSConstants.WSS_SAML2_TOKEN_TYPE + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE ); request.getAny().add(tokenType); @@ -550,26 +537,27 @@ private void runIssueSaml2TokenOnBehalfOfSaml2DifferentRealmFederateIdentity( List securityTokenResponseList = issueToken(issueOperation, request, webServiceContext); - RequestSecurityTokenResponseType securityTokenResponse = securityTokenResponseList.get(0); // Test the generated token. - Element assertion = null; - for (Object tokenObject : securityTokenResponse.getAny()) { - if (tokenObject instanceof JAXBElement - && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { - RequestedSecurityTokenType rstType = - (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); - assertion = (Element)rstType.getAny(); + String jwtToken = null; + for (Object tokenObject : securityTokenResponseList.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); break; } } - assertNotNull(assertion); - String tokenString = DOM2Writer.nodeToString(assertion); - assertTrue(tokenString.contains("AttributeStatement")); - assertTrue(tokenString.contains("ALICE")); //subject changed (to uppercase) - assertTrue(tokenString.contains("doe")); //claim unchanged but requested - assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + // subject changed (to uppercase) + Assert.assertEquals("ALICE", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + // claim unchanged but requested + assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java new file mode 100644 index 00000000000..dc2352f2210 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java @@ -0,0 +1,507 @@ +/** + * 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.sts.operation; + +import java.security.cert.X509Certificate; +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 javax.security.auth.callback.CallbackHandler; +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.ReceivedKey; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.service.ServiceMBean; +import org.apache.cxf.sts.service.StaticService; +import org.apache.cxf.sts.token.delegation.SAMLDelegationHandler; +import org.apache.cxf.sts.token.delegation.TokenDelegationHandler; +import org.apache.cxf.sts.token.delegation.UsernameTokenDelegationHandler; +import org.apache.cxf.sts.token.provider.SAMLTokenProvider; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.realm.RealmProperties; +import org.apache.cxf.sts.token.validator.IssuerSAMLRealmCodec; +import org.apache.cxf.sts.token.validator.SAMLTokenValidator; +import org.apache.cxf.sts.token.validator.TokenValidator; +import org.apache.cxf.sts.token.validator.UsernameTokenValidator; +import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.cxf.ws.security.sts.provider.model.OnBehalfOfType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.secext.AttributedString; +import org.apache.cxf.ws.security.sts.provider.model.secext.PasswordString; +import org.apache.cxf.ws.security.sts.provider.model.secext.UsernameTokenType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.crypto.CryptoType; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; + +/** + * Issue JWT tokens on behalf of other tokens. + */ +public class IssueJWTOnbehalfofUnitTest extends org.junit.Assert { + + public static final QName REQUESTED_SECURITY_TOKEN = + QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(null).getName(); + + /** + * Test to successfully issue a JWT token on-behalf-of a SAML 2 token + */ + @org.junit.Test + public void testIssueJWTTokenOnBehalfOfSaml2() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new JWTTokenProvider()); + issueOperation.setTokenProviders(providerList); + + // Add Token Validator + List validatorList = new ArrayList(); + validatorList.add(new SAMLTokenValidator()); + issueOperation.setTokenValidators(validatorList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + issueOperation.setStsProperties(stsProperties); + + TokenDelegationHandler delegationHandler = new SAMLDelegationHandler(); + issueOperation.setDelegationHandlers(Collections.singletonList(delegationHandler)); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Get a SAML Token via the SAMLTokenProvider + CallbackHandler callbackHandler = new PasswordCallbackHandler(); + Element samlToken = + createSAMLAssertion(WSConstants.WSS_SAML2_TOKEN_TYPE, crypto, "mystskey", callbackHandler); + Document doc = samlToken.getOwnerDocument(); + samlToken = (Element)doc.appendChild(samlToken); + OnBehalfOfType onbehalfof = new OnBehalfOfType(); + onbehalfof.setAny(samlToken); + + JAXBElement onbehalfofType = + new JAXBElement( + QNameConstants.ON_BEHALF_OF, OnBehalfOfType.class, onbehalfof + ); + request.getAny().add(onbehalfofType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + /** + * Test to successfully issue a JWT token on-behalf-of a UsernameToken + */ + @org.junit.Test + public void testIssueJWTTokenOnBehalfOfUsernameToken() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new JWTTokenProvider()); + issueOperation.setTokenProviders(providerList); + + // Add Token Validator + List validatorList = new ArrayList(); + validatorList.add(new UsernameTokenValidator()); + issueOperation.setTokenValidators(validatorList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + issueOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + + // Create a UsernameToken + JAXBElement usernameTokenType = createUsernameToken("alice", "clarinet"); + OnBehalfOfType onbehalfof = new OnBehalfOfType(); + onbehalfof.setAny(usernameTokenType); + + JAXBElement onbehalfofType = + new JAXBElement( + QNameConstants.ON_BEHALF_OF, OnBehalfOfType.class, onbehalfof + ); + request.getAny().add(onbehalfofType); + + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Issue a token + + // This should fail as the default DelegationHandler does not allow UsernameTokens + try { + issueOperation.issue(request, webServiceContext); + fail("Failure expected as UsernameTokens are not accepted for OnBehalfOf by default"); + } catch (STSException ex) { + // expected + } + + TokenDelegationHandler delegationHandler = new UsernameTokenDelegationHandler(); + issueOperation.setDelegationHandlers(Collections.singletonList(delegationHandler)); + + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + /** + * Test to successfully issue a JWT Token (realm "B") on-behalf-of a SAML 2 token + * on-behalf-of token issued by realm "A". + */ + @org.junit.Test + public void testIssueJWTTokenOnBehalfOfSaml2DifferentRealm() throws Exception { + TokenIssueOperation issueOperation = new TokenIssueOperation(); + + // Add Token Provider + List providerList = new ArrayList(); + JWTTokenProvider tokenProvider = new JWTTokenProvider(); + providerList.add(tokenProvider); + issueOperation.setTokenProviders(providerList); + + TokenDelegationHandler delegationHandler = new SAMLDelegationHandler(); + issueOperation.setDelegationHandlers(Collections.singletonList(delegationHandler)); + + // Add Token Validator + List validatorList = new ArrayList(); + SAMLTokenValidator samlTokenValidator = new SAMLTokenValidator(); + samlTokenValidator.setSamlRealmCodec(new IssuerSAMLRealmCodec()); + validatorList.add(samlTokenValidator); + issueOperation.setTokenValidators(validatorList); + + // Add Service + ServiceMBean service = new StaticService(); + service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy")); + issueOperation.setServices(Collections.singletonList(service)); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + stsProperties.setRealmParser(new CustomRealmParser()); + stsProperties.setIdentityMapper(new CustomIdentityMapper()); + issueOperation.setStsProperties(stsProperties); + + Map realms = createSamlRealms(); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Get a SAML Token via the SAMLTokenProvider + CallbackHandler callbackHandler = new PasswordCallbackHandler(); + Element samlToken = + createSAMLAssertion(WSConstants.WSS_SAML2_TOKEN_TYPE, crypto, "mystskey", + callbackHandler, realms, STSConstants.BEARER_KEY_KEYTYPE); + Document doc = samlToken.getOwnerDocument(); + samlToken = (Element)doc.appendChild(samlToken); + OnBehalfOfType onbehalfof = new OnBehalfOfType(); + onbehalfof.setAny(samlToken); + + JAXBElement onbehalfofType = + new JAXBElement( + QNameConstants.ON_BEHALF_OF, OnBehalfOfType.class, onbehalfof + ); + request.getAny().add(onbehalfofType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put("url", "https"); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + tokenProvider.setRealmMap(realms); + + RequestSecurityTokenResponseCollectionType response = + issueOperation.issue(request, webServiceContext); + List securityTokenResponse = + response.getRequestSecurityTokenResponse(); + assertTrue(!securityTokenResponse.isEmpty()); + + // Test the generated token. + String jwtToken = null; + for (Object tokenObject : securityTokenResponse.get(0).getAny()) { + if (tokenObject instanceof Element + && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) + && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { + jwtToken = ((Element)tokenObject).getTextContent(); + break; + } + } + + assertNotNull(jwtToken); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("ALICE", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + /* + * Mock up an SAML assertion element + */ + private Element createSAMLAssertion( + String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler + ) throws WSSecurityException { + return createSAMLAssertion(tokenType, crypto, signatureUsername, + callbackHandler, null, STSConstants.BEARER_KEY_KEYTYPE); + } + + /* + * Mock up an SAML assertion element + */ + private Element createSAMLAssertion( + String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler, + Map realms, String keyType + ) throws WSSecurityException { + SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); + samlTokenProvider.setRealmMap(realms); + + TokenProviderParameters providerParameters = + createProviderParameters( + tokenType, keyType, crypto, signatureUsername, callbackHandler + ); + if (realms != null) { + providerParameters.setRealm("A"); + } + TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return (Element)providerResponse.getToken(); + } + + private TokenProviderParameters createProviderParameters( + String tokenType, String keyType, Crypto crypto, + String signatureUsername, CallbackHandler callbackHandler + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + keyRequirements.setKeyType(keyType); + + CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); + cryptoType.setAlias("myclientkey"); + X509Certificate[] certs = crypto.getX509Certificates(cryptoType); + ReceivedKey receivedKey = new ReceivedKey(); + receivedKey.setX509Cert(certs[0]); + keyRequirements.setReceivedKey(receivedKey); + + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername(signatureUsername); + stsProperties.setCallbackHandler(callbackHandler); + stsProperties.setIssuer("STS"); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setEncryptionCrypto(crypto); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private JAXBElement createUsernameToken(String name, String password) { + UsernameTokenType usernameToken = new UsernameTokenType(); + AttributedString username = new AttributedString(); + username.setValue(name); + usernameToken.setUsername(username); + + // Add a password + if (password != null) { + PasswordString passwordString = new PasswordString(); + passwordString.setValue(password); + passwordString.setType(WSConstants.PASSWORD_TEXT); + JAXBElement passwordType = + new JAXBElement( + QNameConstants.PASSWORD, PasswordString.class, passwordString + ); + usernameToken.getAny().add(passwordType); + } + + JAXBElement tokenType = + new JAXBElement( + QNameConstants.USERNAME_TOKEN, UsernameTokenType.class, usernameToken + ); + + return tokenType; + } + + private Map createSamlRealms() { + // Create Realms + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); + samlRealm.setIssuer("A-Issuer"); + samlRealms.put("A", samlRealm); + samlRealm = new RealmProperties(); + samlRealm.setIssuer("B-Issuer"); + samlRealms.put("B", samlRealm); + return samlRealms; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderActAsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderActAsTest.java new file mode 100644 index 00000000000..9e0fcd64062 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderActAsTest.java @@ -0,0 +1,196 @@ +/** + * 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.sts.token.provider; + +import java.util.Properties; + +import javax.xml.bind.JAXBElement; + +import org.w3c.dom.Element; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.ReceivedToken; +import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.ws.security.sts.provider.model.secext.AttributedString; +import org.apache.cxf.ws.security.sts.provider.model.secext.UsernameTokenType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; + +/** + * Some unit tests for creating JWT Tokens with an ActAs element. + */ +public class JWTProviderActAsTest extends org.junit.Assert { + + /** + * Create a JWT Token with ActAs from a UsernameToken + */ + @org.junit.Test + public void testJWTActAsUsernameToken() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + + UsernameTokenType usernameToken = new UsernameTokenType(); + AttributedString username = new AttributedString(); + username.setValue("bob"); + usernameToken.setUsername(username); + JAXBElement usernameTokenType = + new JAXBElement( + QNameConstants.USERNAME_TOKEN, UsernameTokenType.class, usernameToken + ); + + TokenProviderParameters providerParameters = + createProviderParameters( + JWTTokenProvider.JWT_TOKEN_TYPE, usernameTokenType + ); + //Principal must be set in ReceivedToken/ActAs + providerParameters.getTokenRequirements().getActAs().setPrincipal( + new CustomTokenPrincipal(username.getValue())); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("bob", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + /** + * Create a JWT Token with ActAs from a SAML Assertion + */ + @org.junit.Test + public void testJWTActAsAssertion() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + + String user = "alice"; + Element saml1Assertion = getSAMLAssertion(user); + + TokenProviderParameters providerParameters = + createProviderParameters( + JWTTokenProvider.JWT_TOKEN_TYPE, saml1Assertion + ); + //Principal must be set in ReceivedToken/ActAs + providerParameters.getTokenRequirements().getActAs().setPrincipal( + new CustomTokenPrincipal(user)); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals(user, jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + private Element getSAMLAssertion(String user) throws Exception { + TokenProvider samlTokenProvider = new SAMLTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(WSConstants.WSS_SAML_TOKEN_TYPE, null); + providerParameters.getKeyRequirements().setKeyType(STSConstants.BEARER_KEY_KEYTYPE); + providerParameters.setPrincipal(new CustomTokenPrincipal(user)); + assertTrue(samlTokenProvider.canHandleToken(WSConstants.WSS_SAML_TOKEN_TYPE)); + TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return (Element)providerResponse.getToken(); + } + + private TokenProviderParameters createProviderParameters( + String tokenType, Object actAs + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + + if (actAs != null) { + ReceivedToken actAsToken = new ReceivedToken(actAs); + actAsToken.setState(STATE.VALID); + tokenRequirements.setActAs(actAsToken); + + } + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderOnBehalfOfTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderOnBehalfOfTest.java new file mode 100644 index 00000000000..265b48c22f3 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTProviderOnBehalfOfTest.java @@ -0,0 +1,196 @@ +/** + * 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.sts.token.provider; + +import java.util.Properties; + +import javax.xml.bind.JAXBElement; + +import org.w3c.dom.Element; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.ReceivedToken; +import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.ws.security.sts.provider.model.secext.AttributedString; +import org.apache.cxf.ws.security.sts.provider.model.secext.UsernameTokenType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; + +/** + * Some unit tests for creating JWT Tokens with an OnBehalfOf element. + */ +public class JWTProviderOnBehalfOfTest extends org.junit.Assert { + + /** + * Create a JWT Token with OnBehalfOf from a UsernameToken + */ + @org.junit.Test + public void testJWTOnBehalfOfUsernameToken() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + + UsernameTokenType usernameToken = new UsernameTokenType(); + AttributedString username = new AttributedString(); + username.setValue("bob"); + usernameToken.setUsername(username); + JAXBElement usernameTokenType = + new JAXBElement( + QNameConstants.USERNAME_TOKEN, UsernameTokenType.class, usernameToken + ); + + TokenProviderParameters providerParameters = + createProviderParameters( + JWTTokenProvider.JWT_TOKEN_TYPE, usernameTokenType + ); + //Principal must be set in ReceivedToken/OnBehalfOf + providerParameters.getTokenRequirements().getOnBehalfOf().setPrincipal( + new CustomTokenPrincipal(username.getValue())); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("bob", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + /** + * Create a JWT Token with OnBehalfOf from a SAML Assertion + */ + @org.junit.Test + public void testJWTOnBehalfOfAssertion() throws Exception { + TokenProvider tokenProvider = new JWTTokenProvider(); + + String user = "alice"; + Element saml1Assertion = getSAMLAssertion(user); + + TokenProviderParameters providerParameters = + createProviderParameters( + JWTTokenProvider.JWT_TOKEN_TYPE, saml1Assertion + ); + //Principal must be set in ReceivedToken/OnBehalfOf + providerParameters.getTokenRequirements().getOnBehalfOf().setPrincipal( + new CustomTokenPrincipal(user)); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals(user, jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + private Element getSAMLAssertion(String user) throws Exception { + TokenProvider samlTokenProvider = new SAMLTokenProvider(); + TokenProviderParameters providerParameters = + createProviderParameters(WSConstants.WSS_SAML_TOKEN_TYPE, null); + providerParameters.getKeyRequirements().setKeyType(STSConstants.BEARER_KEY_KEYTYPE); + providerParameters.setPrincipal(new CustomTokenPrincipal(user)); + assertTrue(samlTokenProvider.canHandleToken(WSConstants.WSS_SAML_TOKEN_TYPE)); + TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return (Element)providerResponse.getToken(); + } + + private TokenProviderParameters createProviderParameters( + String tokenType, Object onBehalfOf + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + + if (onBehalfOf != null) { + ReceivedToken onBehalfOfToken = new ReceivedToken(onBehalfOf); + onBehalfOfToken.setState(STATE.VALID); + tokenRequirements.setOnBehalfOf(onBehalfOfToken); + + } + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + + +} From 9320bea658e97e54d2bd9d47ad07313c388079f9 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 10 Nov 2015 12:14:54 +0000 Subject: [PATCH 0033/1346] Removing unused variable --- parent/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 751f730ef1b..8244caff7b6 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -76,7 +76,6 @@ 2.2_2 [2.1.3,3.0.0) 1.10 - 3.2.1 2.6 3.4 10.2.2.0 From 772ca96b6c3e2ee569b538e8aebdb80777c900d7 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 10 Nov 2015 13:20:33 +0000 Subject: [PATCH 0034/1346] Fixing an openid scope constant --- .../cxf/rs/security/oidc/utils/OidcUtils.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/utils/OidcUtils.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/utils/OidcUtils.java index 7ced7175606..42837fd66aa 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/utils/OidcUtils.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/utils/OidcUtils.java @@ -33,7 +33,7 @@ public final class OidcUtils { public static final String ID_TOKEN = "id_token"; - public static final String OIDC_SCOPE = "oidc"; + public static final String OPENID_SCOPE = "openid"; public static final String PROFILE_SCOPE = "profile"; public static final String EMAIL_SCOPE = "email"; public static final String ADDRESS_SCOPE = "address"; @@ -56,23 +56,23 @@ public final class OidcUtils { private OidcUtils() { } - public static String getOidcScope() { - return OIDC_SCOPE; + public static String getOpenIdScope() { + return OPENID_SCOPE; } public static String getProfileScope() { - return getScope(OIDC_SCOPE, PROFILE_SCOPE); + return getScope(OPENID_SCOPE, PROFILE_SCOPE); } public static String getEmailScope() { - return getScope(OIDC_SCOPE, EMAIL_SCOPE); + return getScope(OPENID_SCOPE, EMAIL_SCOPE); } public static String getAddressScope() { - return getScope(OIDC_SCOPE, ADDRESS_SCOPE); + return getScope(OPENID_SCOPE, ADDRESS_SCOPE); } public static String getPhoneScope() { - return getScope(OIDC_SCOPE, PHONE_SCOPE); + return getScope(OPENID_SCOPE, PHONE_SCOPE); } public static String getAllScopes() { - return getScope(OIDC_SCOPE, PROFILE_SCOPE, EMAIL_SCOPE, ADDRESS_SCOPE, PHONE_SCOPE); + return getScope(OPENID_SCOPE, PROFILE_SCOPE, EMAIL_SCOPE, ADDRESS_SCOPE, PHONE_SCOPE); } public static List getScopeProperties(String scope) { return SCOPES_MAP.get(scope); From f8df21ed3d17539a81c9cdfdc4222d13ffbe62cd Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 10 Nov 2015 17:19:19 +0000 Subject: [PATCH 0035/1346] Switch to use StandardCharset.UTF_8 instead of string literal --- .../src/main/java/org/apache/cxf/BusFactory.java | 3 ++- .../cxf/attachment/AttachmentSerializer.java | 5 +++-- .../apache/cxf/attachment/AttachmentUtil.java | 8 +++++--- .../org/apache/cxf/attachment/Rfc5987Util.java | 3 ++- .../extension/TextExtensionFragmentParser.java | 3 ++- .../cxf/bus/spring/BusApplicationContext.java | 3 ++- .../apache/cxf/common/jaxb/JAXBContextCache.java | 3 ++- .../org/apache/cxf/common/jaxb/JAXBUtils.java | 5 +++-- .../apache/cxf/common/util/Base64UrlUtility.java | 8 ++------ .../apache/cxf/common/util/Base64Utility.java | 6 ++---- .../org/apache/cxf/common/util/StringUtils.java | 3 ++- .../apache/cxf/common/util/URIParserUtil.java | 16 ++++++---------- .../org/apache/cxf/common/util/UrlUtils.java | 7 ++++--- .../org/apache/cxf/helpers/HttpHeaderHelper.java | 2 +- .../AbstractOutDatabindingInterceptor.java | 3 ++- .../cxf/interceptor/StaxOutInterceptor.java | 3 ++- .../org/apache/cxf/io/CachedOutputStream.java | 5 +++-- .../java/org/apache/cxf/io/CachedWriter.java | 3 ++- .../java/org/apache/cxf/staxutils/StaxUtils.java | 5 +++-- .../attachment/AttachmentDeserializerTest.java | 5 +++-- .../apache/cxf/attachment/Rfc5987UtilTest.java | 5 +++-- .../cxf/common/util/Base64UtilityTest.java | 5 +++-- .../org/apache/cxf/io/CachedStreamTestBase.java | 5 +++-- .../transform/OutTransformWriterTest.java | 9 +++++---- .../staxutils/transform/TransformTestUtils.java | 3 ++- .../controllers/ApplicationController.java | 2 +- .../controllers/MemoryOAuthDataProvider.java | 2 +- .../org/apache/cxf/jaxb/io/DataWriterImpl.java | 3 ++- .../cxf/ext/logging/LoggingInInterceptor.java | 3 ++- .../apache/cxf/ext/logging/SOAPLoggingTest.java | 9 +++++---- .../org/apache/cxf/jaxrs/impl/ResponseImpl.java | 3 ++- .../jaxrs/interceptor/JAXRSOutInterceptor.java | 5 +++-- .../cxf/jaxrs/provider/BinaryDataProvider.java | 3 ++- .../cxf/jaxrs/provider/DataBindingProvider.java | 3 ++- .../cxf/jaxrs/provider/FormEncodingProvider.java | 5 +++-- .../cxf/jaxrs/provider/JAXBElementProvider.java | 3 ++- .../jaxrs/provider/PrimitiveTextProvider.java | 5 +++-- .../cxf/jaxrs/provider/SourceProvider.java | 3 ++- .../cxf/jaxrs/provider/StringTextProvider.java | 5 +++-- .../cxf/jaxrs/provider/XSLTJaxbProvider.java | 3 ++- .../org/apache/cxf/jaxrs/utils/FormUtils.java | 8 ++++---- .../org/apache/cxf/jaxrs/utils/HttpUtils.java | 5 +++-- .../org/apache/cxf/jaxrs/utils/JAXRSUtils.java | 3 ++- .../apache/cxf/jaxrs/utils/ResourceUtils.java | 3 ++- .../cxf/jaxrs/utils/schemas/SchemaHandler.java | 3 ++- .../apache/cxf/jaxrs/impl/RequestImplTest.java | 13 +++++++------ .../jaxrs/provider/FormEncodingProviderTest.java | 3 ++- .../jaxrs/provider/JAXBElementProviderTest.java | 7 ++++--- .../provider/PrimitiveTextProviderTest.java | 3 ++- .../apache/cxf/jaxrs/utils/FormUtilsTest.java | 10 +++++++--- .../apache/cxf/jaxrs/utils/JAXRSUtilsTest.java | 5 +++-- .../jaxws/interceptors/SwAOutInterceptor.java | 9 +++------ .../cxf/js/rhino/JsServiceFactoryBean.java | 3 ++- .../provider/aegis/AegisElementProvider.java | 3 ++- .../jaxrs/provider/aegis/AegisJSONProvider.java | 3 ++- .../cxf/jaxrs/provider/json/JSONProvider.java | 5 +++-- .../cxf/jaxrs/provider/json/utils/JSONUtils.java | 3 ++- .../jsonp/AbstractJsonpOutInterceptor.java | 3 ++- .../jsonp/JsonpJaxrsWriterInterceptor.java | 3 ++- .../provider/atom/AtomPojoProviderTest.java | 6 +++--- .../jaxrs/provider/dom4j/DOM4JProviderTest.java | 5 +++-- .../ext/search/hbase/HBaseQueryVisitor.java | 15 ++++----------- .../jose/jaxrs/AbstractJweDecryptingFilter.java | 4 +++- .../jose/jaxrs/JwsJsonWriterInterceptor.java | 3 ++- .../jose/jaxrs/JwsWriterInterceptor.java | 3 ++- .../cxf/rs/security/jose/common/JoseUtils.java | 9 ++------- .../rs/security/jose/jwe/JweCompactConsumer.java | 9 ++------- .../security/jose/jwe/JweDecryptionOutput.java | 10 ++-------- .../rs/security/jose/jwe/JweJsonConsumer.java | 9 ++------- .../security/jose/jwe/JweJwtCompactConsumer.java | 3 ++- .../PbesHmacAesWrapKeyEncryptionAlgorithm.java | 4 ++-- .../cxf/rs/security/jose/jwk/JwkUtils.java | 3 ++- .../jose/jwe/JweCompactReaderWriterTest.java | 11 ++++++----- .../security/jose/jwe/JwePbeHmacAesWrapTest.java | 5 +++-- .../cxf/rs/security/oauth/utils/OAuthUtils.java | 3 ++- .../grants/saml/Saml2BearerGrantHandler.java | 6 ++++-- .../oauth2/client/ClientCodeRequestFilter.java | 5 +++-- .../security/oauth2/client/OAuthClientUtils.java | 3 ++- .../oauth2/grants/jwt/JwtBearerGrantHandler.java | 3 ++- .../oauth2/provider/OAuthJSONProvider.java | 5 +++-- .../oidc/rp/OidcIdTokenRequestFilter.java | 5 +++-- .../oidc/rp/OidcRpAuthenticationFilter.java | 5 +++-- .../AbstractRequestAssertionConsumerHandler.java | 12 ++++-------- .../saml/sso/AbstractServiceProviderFilter.java | 3 ++- .../cxf/rs/security/saml/sso/MetadataWriter.java | 3 ++- .../saml/sso/SAMLProtocolResponseValidator.java | 3 ++- .../security/saml/sso/SamlPostBindingFilter.java | 5 +++-- .../saml/sso/SamlRedirectBindingFilter.java | 13 +++++++------ .../rs/security/saml/AbstractSamlInHandler.java | 3 ++- .../saml/AbstractSamlOutInterceptor.java | 10 +++------- .../rs/security/saml/SamlEnvelopedInHandler.java | 3 ++- .../rs/security/xml/AbstractXmlEncInHandler.java | 3 ++- .../rs/security/xml/AbstractXmlSecInHandler.java | 3 ++- .../rs/security/xml/XmlSecOutInterceptor.java | 3 ++- .../cxf/rt/security/crypto/CryptoUtils.java | 5 +++-- .../apache/cxf/rt/security/crypto/HmacUtils.java | 8 ++------ .../rt/security/crypto/MessageDigestUtils.java | 6 ++---- .../http_jetty/JettyHTTPDestinationTest.java | 5 +++-- .../transport/http/netty/server/util/Utils.java | 5 +++-- .../netty/server/NettyHttpDestinationTest.java | 5 +++-- .../httpclient/PublicSuffixMatcherLoader.java | 3 ++- .../cxf/transport/jms/JMSMessageUtils.java | 3 ++- .../cxf/transport/jms/uri/JMSURIParser.java | 3 ++- .../cxf/transport/jms/JMSMessageUtilTest.java | 7 +++++-- .../org/apache/cxf/ws/rm/CapturingXMLWriter.java | 3 ++- .../cxf/ws/rm/soap/RetransmissionQueueImpl.java | 3 ++- .../security/wss4j/WSS4JStaxOutInterceptor.java | 3 ++- .../org/apache/cxf/wsdl/JAXBExtensionHelper.java | 3 ++- .../cxf/wsdl11/ServiceWSDLBuilderTest.java | 3 ++- .../cxf/ws/discovery/WSDiscoveryClientTest.java | 5 +++-- .../systest/jaxrs/JAXRSClientServerBookTest.java | 3 ++- .../JAXRSClientServerProxySpringBookTest.java | 3 ++- ...tServerResourceCreatedSpringProviderTest.java | 5 +++-- .../jaxrs/JAXRSClientServerSpringBookTest.java | 9 +++++---- .../systest/jaxrs/JAXRSLoggingAtomPushTest.java | 3 ++- .../systest/handlers/SoapFaultHandlerTest.java | 3 ++- .../cxf/systest/swa/SwANoMimeServiceImpl.java | 14 +++----------- .../jaxrs/kerberos/JAXRSKerberosBookTest.java | 3 ++- .../systest/kerberos/ldap/LDAPClaimsTest.java | 3 ++- .../wssec/kerberos/KerberosTokenTest.java | 3 ++- .../kerberos/wssec/spnego/SpnegoTokenTest.java | 3 ++- .../security/oauth/MemoryOAuthDataProvider.java | 3 ++- .../cxf/systest/servlet/CXFFilterTest.java | 3 ++- .../cxf/systest/servlet/CXFServletTest.java | 3 ++- .../servlet/ExternalServicesServletTest.java | 3 ++- .../systest/servlet/JsFrontEndServletTest.java | 2 +- .../java/org/apache/cxf/systest/js/Server.java | 5 +++-- .../apache/cxf/systest/mtom/MtomServerTest.java | 5 +++-- .../apache/cxf/tools/util/FileWriterUtil.java | 5 +++-- .../apache/cxf/tools/util/URIParserUtilTest.java | 5 +++-- .../corba/processors/idl/IDLToWSDLProcessor.java | 3 ++- .../apache/cxf/tools/corba/WSDLToIDLTest.java | 7 ++++--- .../wsdl11/DateTypeCustomGenerator.java | 3 ++- .../generator/wsdl11/WSDL11Generator.java | 8 +++++--- .../cxf/tools/wadlto/jaxrs/JAXRSContainer.java | 3 ++- .../cxf/tools/wadlto/jaxrs/SourceGenerator.java | 10 ++++++---- .../cxf/tools/wsdlto/WSDLToJavaContainer.java | 3 ++- .../wsdlto/javascript/WSDLToJavaScriptTest.java | 5 +++-- .../cxf/tools/wsdlto/jaxws/CodeGenBugTest.java | 3 ++- 139 files changed, 371 insertions(+), 306 deletions(-) diff --git a/core/src/main/java/org/apache/cxf/BusFactory.java b/core/src/main/java/org/apache/cxf/BusFactory.java index 1019fcfb44c..7f1dd01021c 100644 --- a/core/src/main/java/org/apache/cxf/BusFactory.java +++ b/core/src/main/java/org/apache/cxf/BusFactory.java @@ -22,6 +22,7 @@ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; @@ -378,7 +379,7 @@ private static String getBusFactoryClass(ClassLoader classLoader) { } if (is != null) { - try (BufferedReader rd = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { + try (BufferedReader rd = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { busFactoryClass = rd.readLine(); busFactoryCondition = rd.readLine(); } diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java index 539fcc603eb..d01f209f5e2 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentSerializer.java @@ -24,6 +24,7 @@ import java.io.StringWriter; import java.io.Writer; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; @@ -144,7 +145,7 @@ public void writeProlog() throws IOException { out = message.getContent(OutputStream.class); encoding = (String) message.get(Message.ENCODING); if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); } StringWriter writer = new StringWriter(); writer.write("--"); @@ -200,7 +201,7 @@ private static void writeHeaders(String contentType, String attachmentId, if (attachmentId != null) { attachmentId = checkAngleBrackets(attachmentId); writer.write("Content-ID: <"); - writer.write(URLDecoder.decode(attachmentId, "UTF-8")); + writer.write(URLDecoder.decode(attachmentId, StandardCharsets.UTF_8.name())); writer.write(">\r\n"); } // headers like Content-Disposition need to be serialized diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java index 11ff76ffc17..ec3407a7bc7 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java @@ -28,6 +28,7 @@ import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; @@ -209,7 +210,8 @@ public static String createContentID(String ns) throws UnsupportedEncodingExcept cid = ns; } } - return URLEncoder.encode(name, "UTF-8") + "@" + URLEncoder.encode(cid, "UTF-8"); + return URLEncoder.encode(name, StandardCharsets.UTF_8.name()) + "@" + + URLEncoder.encode(cid, StandardCharsets.UTF_8.name()); } public static String getUniqueBoundaryValue() { @@ -335,7 +337,7 @@ public static String cleanContentId(String id) { } // urldecode. Is this bad even without cid:? What does decode do with malformed %-signs, anyhow? try { - id = URLDecoder.decode(id, "UTF-8"); + id = URLDecoder.decode(id, StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { //ignore, keep id as is } @@ -526,7 +528,7 @@ public static DataSource getAttachmentDataSource(String contentId, Collection getExtensions(final URL url) { */ public List getExtensions(InputStream is) throws IOException { List extensions = new ArrayList(); - BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); String line = reader.readLine(); while (line != null) { final Extension extension = getExtensionFromTextLine(line); diff --git a/core/src/main/java/org/apache/cxf/bus/spring/BusApplicationContext.java b/core/src/main/java/org/apache/cxf/bus/spring/BusApplicationContext.java index dc37c2cfa88..36d951b0811 100644 --- a/core/src/main/java/org/apache/cxf/bus/spring/BusApplicationContext.java +++ b/core/src/main/java/org/apache/cxf/bus/spring/BusApplicationContext.java @@ -25,6 +25,7 @@ import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; @@ -156,7 +157,7 @@ protected Resource[] getConfigResources() { Resource[] exts = resolver.getResources(DEFAULT_CXF_EXT_CFG_FILE); for (Resource r : exts) { try (InputStream is = r.getInputStream(); - BufferedReader rd = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { + BufferedReader rd = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { String line = rd.readLine(); while (line != null) { if (!"".equals(line)) { diff --git a/core/src/main/java/org/apache/cxf/common/jaxb/JAXBContextCache.java b/core/src/main/java/org/apache/cxf/common/jaxb/JAXBContextCache.java index 55bcd789554..d6393d2e2fd 100644 --- a/core/src/main/java/org/apache/cxf/common/jaxb/JAXBContextCache.java +++ b/core/src/main/java/org/apache/cxf/common/jaxb/JAXBContextCache.java @@ -24,6 +24,7 @@ import java.io.InputStreamReader; import java.lang.ref.WeakReference; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -403,7 +404,7 @@ public static void addPackage(Set> classes, String pkg, ClassLoader lo //ignore } try (InputStream ins = loader.getResourceAsStream("/" + pkg.replace('.', '/') + "/jaxb.index"); - BufferedReader reader = new BufferedReader(new InputStreamReader(ins, "UTF-8"))) { + BufferedReader reader = new BufferedReader(new InputStreamReader(ins, StandardCharsets.UTF_8))) { if (!StringUtils.isEmpty(pkg)) { pkg += "."; } diff --git a/core/src/main/java/org/apache/cxf/common/jaxb/JAXBUtils.java b/core/src/main/java/org/apache/cxf/common/jaxb/JAXBUtils.java index f4e4b5492d6..6440c37b196 100644 --- a/core/src/main/java/org/apache/cxf/common/jaxb/JAXBUtils.java +++ b/core/src/main/java/org/apache/cxf/common/jaxb/JAXBUtils.java @@ -37,6 +37,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -765,7 +766,7 @@ public static List getGeneratedClassNames(JCodeModel codeModel) { return classes; } public static Object createFileCodeWriter(File f) throws JAXBException { - return createFileCodeWriter(f, "UTF-8"); + return createFileCodeWriter(f, StandardCharsets.UTF_8.name()); } public static Object createFileCodeWriter(File f, String encoding) throws JAXBException { try { @@ -896,7 +897,7 @@ public static void scanPackages(Set> classes, if (entry.getValue() != null) { BufferedReader reader = null; try { - reader = new BufferedReader(new InputStreamReader(entry.getValue(), "UTF-8")); + reader = new BufferedReader(new InputStreamReader(entry.getValue(), StandardCharsets.UTF_8)); String pkg = entry.getKey(); ClassLoader loader = packageLoaders.get(pkg); if (!StringUtils.isEmpty(pkg)) { diff --git a/core/src/main/java/org/apache/cxf/common/util/Base64UrlUtility.java b/core/src/main/java/org/apache/cxf/common/util/Base64UrlUtility.java index 25dc0df56e9..cde97017425 100644 --- a/core/src/main/java/org/apache/cxf/common/util/Base64UrlUtility.java +++ b/core/src/main/java/org/apache/cxf/common/util/Base64UrlUtility.java @@ -29,7 +29,7 @@ import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; public final class Base64UrlUtility { @@ -42,11 +42,7 @@ public static byte[] decode(String encoded) throws Base64Exception { } public static String encode(String str) { - try { - return encode(str.getBytes("UTF-8")); - } catch (UnsupportedEncodingException ex) { - throw new RuntimeException(ex); - } + return encode(str.getBytes(StandardCharsets.UTF_8)); } public static String encode(byte[] id) { diff --git a/core/src/main/java/org/apache/cxf/common/util/Base64Utility.java b/core/src/main/java/org/apache/cxf/common/util/Base64Utility.java index 1888d693467..d101b39f8aa 100644 --- a/core/src/main/java/org/apache/cxf/common/util/Base64Utility.java +++ b/core/src/main/java/org/apache/cxf/common/util/Base64Utility.java @@ -29,7 +29,7 @@ import java.io.OutputStream; import java.io.Writer; import java.nio.CharBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.logging.Logger; @@ -85,8 +85,6 @@ public final class Base64Utility { private static final int PAD_SIZE4 = 2; private static final int PAD_SIZE8 = 3; - private static final Charset CHARSET_UTF8 = Charset.forName("UTF-8"); - // class static initializer for building decode table static { for (int i = 0; i < BDTSIZE; i++) { @@ -379,7 +377,7 @@ public static void encodeAndStream(byte[] id, private static void writeCharArrayToStream(char[] chunk, int len, OutputStream os) throws IOException { // may be we can just cast to byte when creating chunk[] earlier on - byte[] bytes = CHARSET_UTF8.encode(CharBuffer.wrap(chunk, 0, len)).array(); + byte[] bytes = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chunk, 0, len)).array(); os.write(bytes); } diff --git a/core/src/main/java/org/apache/cxf/common/util/StringUtils.java b/core/src/main/java/org/apache/cxf/common/util/StringUtils.java index 23e39e00cfb..b90c078add3 100644 --- a/core/src/main/java/org/apache/cxf/common/util/StringUtils.java +++ b/core/src/main/java/org/apache/cxf/common/util/StringUtils.java @@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -197,7 +198,7 @@ public static String uncapitalize(String str) { } public static byte[] toBytesUTF8(String str) { - return toBytes(str, "UTF-8"); + return toBytes(str, StandardCharsets.UTF_8.name()); } public static byte[] toBytesASCII(String str) { return toBytes(str, "US-ASCII"); diff --git a/core/src/main/java/org/apache/cxf/common/util/URIParserUtil.java b/core/src/main/java/org/apache/cxf/common/util/URIParserUtil.java index 8170709bbf8..9850ee5ada5 100644 --- a/core/src/main/java/org/apache/cxf/common/util/URIParserUtil.java +++ b/core/src/main/java/org/apache/cxf/common/util/URIParserUtil.java @@ -20,11 +20,11 @@ package org.apache.cxf.common.util; import java.io.File; -import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; @@ -222,15 +222,11 @@ public static String escapeChars(String s) { for (int x = 0; x < s.length(); x++) { char ch = s.charAt(x); if (isExcluded(ch)) { - try { - byte[] bytes = Character.toString(ch).getBytes("UTF-8"); - for (int y = 0; y < bytes.length; y++) { - b.append("%"); - b.append(HEX_DIGITS.charAt((bytes[y] & 0xFF) >> 4)); - b.append(HEX_DIGITS.charAt(bytes[y] & 0x0F)); - } - } catch (UnsupportedEncodingException e) { - //should not happen + byte[] bytes = Character.toString(ch).getBytes(StandardCharsets.UTF_8); + for (int y = 0; y < bytes.length; y++) { + b.append("%"); + b.append(HEX_DIGITS.charAt((bytes[y] & 0xFF) >> 4)); + b.append(HEX_DIGITS.charAt(bytes[y] & 0x0F)); } } else { b.append(ch); diff --git a/core/src/main/java/org/apache/cxf/common/util/UrlUtils.java b/core/src/main/java/org/apache/cxf/common/util/UrlUtils.java index 8b2518e599f..3dd0087b5e8 100644 --- a/core/src/main/java/org/apache/cxf/common/util/UrlUtils.java +++ b/core/src/main/java/org/apache/cxf/common/util/UrlUtils.java @@ -23,6 +23,7 @@ import java.net.URLEncoder; import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; @@ -43,7 +44,7 @@ private UrlUtils() { public static String urlEncode(String value) { - return urlEncode(value, "UTF-8"); + return urlEncode(value, StandardCharsets.UTF_8.name()); } public static String urlEncode(String value, String enc) { @@ -118,7 +119,7 @@ private static int digit16(final byte b) { public static String urlDecode(String value) { - return urlDecode(value, "UTF-8"); + return urlDecode(value, StandardCharsets.UTF_8.name()); } /** @@ -127,7 +128,7 @@ public static String urlDecode(String value) { * @param value value to decode */ public static String pathDecode(String value) { - return urlDecode(value, "UTF-8", true); + return urlDecode(value, StandardCharsets.UTF_8.name(), true); } diff --git a/core/src/main/java/org/apache/cxf/helpers/HttpHeaderHelper.java b/core/src/main/java/org/apache/cxf/helpers/HttpHeaderHelper.java index 049691d1660..62dec05bb70 100644 --- a/core/src/main/java/org/apache/cxf/helpers/HttpHeaderHelper.java +++ b/core/src/main/java/org/apache/cxf/helpers/HttpHeaderHelper.java @@ -108,7 +108,7 @@ public static String mapCharset(String enc, String deflt) { return deflt; } //older versions of tomcat don't properly parse ContentType headers with stuff - //after charset="UTF-8" + //after charset=StandardCharsets.UTF_8 int idx = enc.indexOf(";"); if (idx != -1) { enc = enc.substring(0, idx); diff --git a/core/src/main/java/org/apache/cxf/interceptor/AbstractOutDatabindingInterceptor.java b/core/src/main/java/org/apache/cxf/interceptor/AbstractOutDatabindingInterceptor.java index 0993e4bf985..0e3183b915e 100644 --- a/core/src/main/java/org/apache/cxf/interceptor/AbstractOutDatabindingInterceptor.java +++ b/core/src/main/java/org/apache/cxf/interceptor/AbstractOutDatabindingInterceptor.java @@ -20,6 +20,7 @@ package org.apache.cxf.interceptor; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -178,7 +179,7 @@ protected boolean writeToOutputStream(Message m, BindingInfo info, Service s) { return info.getClass().getName().equals("org.apache.cxf.binding.soap.model.SoapBindingInfo") && s.getDataBinding().getClass().getName().equals("org.apache.cxf.jaxb.JAXBDataBinding") && !MessageUtils.isDOMPresent(m) - && (enc == null || "UTF-8".equals(enc)); + && (enc == null || StandardCharsets.UTF_8.name().equals(enc)); } protected DataWriter getDataWriter(Message message, Service service, Class output) { diff --git a/core/src/main/java/org/apache/cxf/interceptor/StaxOutInterceptor.java b/core/src/main/java/org/apache/cxf/interceptor/StaxOutInterceptor.java index 9de2dac4eb8..85090368671 100644 --- a/core/src/main/java/org/apache/cxf/interceptor/StaxOutInterceptor.java +++ b/core/src/main/java/org/apache/cxf/interceptor/StaxOutInterceptor.java @@ -21,6 +21,7 @@ import java.io.OutputStream; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; @@ -137,7 +138,7 @@ private String getEncoding(Message message) { } if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); message.put(Message.ENCODING, encoding); } return encoding; diff --git a/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java b/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java index 9464b516eea..e71adff668b 100644 --- a/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java +++ b/core/src/main/java/org/apache/cxf/io/CachedOutputStream.java @@ -31,6 +31,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; @@ -310,7 +311,7 @@ public void writeCacheTo(OutputStream out) throws IOException { } public void writeCacheTo(StringBuilder out, long limit) throws IOException { - writeCacheTo(out, "UTF-8", limit); + writeCacheTo(out, StandardCharsets.UTF_8.name(), limit); } public void writeCacheTo(StringBuilder out, String charsetName, long limit) throws IOException { @@ -356,7 +357,7 @@ public void writeCacheTo(StringBuilder out, String charsetName, long limit) thro } public void writeCacheTo(StringBuilder out) throws IOException { - writeCacheTo(out, "UTF-8"); + writeCacheTo(out, StandardCharsets.UTF_8.name()); } public void writeCacheTo(StringBuilder out, String charsetName) throws IOException { diff --git a/core/src/main/java/org/apache/cxf/io/CachedWriter.java b/core/src/main/java/org/apache/cxf/io/CachedWriter.java index 8d33cdb87bb..8fb3fdfde3c 100644 --- a/core/src/main/java/org/apache/cxf/io/CachedWriter.java +++ b/core/src/main/java/org/apache/cxf/io/CachedWriter.java @@ -33,6 +33,7 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; @@ -517,7 +518,7 @@ public void close() throws IOException { } }; } - return new InputStreamReader(fileInputStream, "UTF-8"); + return new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); } catch (FileNotFoundException e) { throw new IOException("Cached file was deleted, " + e.toString()); } diff --git a/core/src/main/java/org/apache/cxf/staxutils/StaxUtils.java b/core/src/main/java/org/apache/cxf/staxutils/StaxUtils.java index d7c2720c8f2..6b0eb42f333 100644 --- a/core/src/main/java/org/apache/cxf/staxutils/StaxUtils.java +++ b/core/src/main/java/org/apache/cxf/staxutils/StaxUtils.java @@ -28,6 +28,7 @@ import java.io.StringWriter; import java.io.Writer; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -382,7 +383,7 @@ public static XMLStreamWriter createXMLStreamWriter(OutputStream out) { public static XMLStreamWriter createXMLStreamWriter(OutputStream out, String encoding) { if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); } XMLOutputFactory factory = getXMLOutputFactory(); try { @@ -1695,7 +1696,7 @@ public static XMLStreamReader createXMLStreamReader(InputSource src) { */ public static XMLStreamReader createXMLStreamReader(InputStream in, String encoding) { if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); } XMLInputFactory factory = getXMLInputFactory(); diff --git a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java index 401413577d9..330ec9fb940 100644 --- a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java +++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -84,7 +85,7 @@ public void testNoBoundaryInCT() throws Exception { Assert.assertTrue(m.find()); msg = new MessageImpl(); - msg.setContent(InputStream.class, new ByteArrayInputStream(message.getBytes("UTF-8"))); + msg.setContent(InputStream.class, new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8))); msg.put(Message.CONTENT_TYPE, "multipart/related"); AttachmentDeserializer ad = new AttachmentDeserializer(msg); ad.initializeAttachments(); @@ -427,7 +428,7 @@ public void testDoesntReturnZero() throws Exception { + "------=_Part_1" + "\n\nContent-Transfer-Encoding: binary\n\n" + "ABCD3\r\n" - + "------=_Part_1--").getBytes("UTF-8"); + + "------=_Part_1--").getBytes(StandardCharsets.UTF_8); ByteArrayInputStream in = new ByteArrayInputStream(messageBytes) { public int read(byte[] b, int off, int len) { return super.read(b, off, len >= 2 ? 2 : len); diff --git a/core/src/test/java/org/apache/cxf/attachment/Rfc5987UtilTest.java b/core/src/test/java/org/apache/cxf/attachment/Rfc5987UtilTest.java index 17f04f05fd8..e7309094104 100644 --- a/core/src/test/java/org/apache/cxf/attachment/Rfc5987UtilTest.java +++ b/core/src/test/java/org/apache/cxf/attachment/Rfc5987UtilTest.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.attachment; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -49,8 +50,8 @@ public static List params() throws Exception { @Test public void test() throws Exception { - assertEquals(expected, Rfc5987Util.encode(input, "UTF-8")); + assertEquals(expected, Rfc5987Util.encode(input, StandardCharsets.UTF_8.name())); - assertEquals(input, Rfc5987Util.decode(expected, "UTF-8")); + assertEquals(input, Rfc5987Util.decode(expected, StandardCharsets.UTF_8.name())); } } diff --git a/core/src/test/java/org/apache/cxf/common/util/Base64UtilityTest.java b/core/src/test/java/org/apache/cxf/common/util/Base64UtilityTest.java index 213dcd0fb7c..a05ffafee05 100644 --- a/core/src/test/java/org/apache/cxf/common/util/Base64UtilityTest.java +++ b/core/src/test/java/org/apache/cxf/common/util/Base64UtilityTest.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import org.apache.cxf.helpers.IOUtils; @@ -43,7 +44,7 @@ void assertEquals(byte b1[], byte b2[]) { @Test public void testEncodeMultipleChunks() throws Exception { final String text = "The true sign of intelligence is not knowledge but imagination."; - byte[] bytes = text.getBytes("UTF-8"); + byte[] bytes = text.getBytes(StandardCharsets.UTF_8); // multiple of 3 octets assertEquals(63, bytes.length); String s1 = new String(Base64Utility.encodeChunk(bytes, 0, bytes.length)); @@ -64,7 +65,7 @@ public void testEncodeMultipleChunks() throws Exception { public void testEncodeAndStream() throws Exception { final String text = "The true sign of intelligence is not knowledge but imagination."; ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] bytes = text.getBytes("UTF-8"); + byte[] bytes = text.getBytes(StandardCharsets.UTF_8); Base64Utility.encodeAndStream(bytes, 0, bytes.length, bos); String decodedText = new String(Base64Utility.decode(bos.toString())); assertEquals(decodedText, text); diff --git a/core/src/test/java/org/apache/cxf/io/CachedStreamTestBase.java b/core/src/test/java/org/apache/cxf/io/CachedStreamTestBase.java index 5a827921f9b..9d69fe356ac 100755 --- a/core/src/test/java/org/apache/cxf/io/CachedStreamTestBase.java +++ b/core/src/test/java/org/apache/cxf/io/CachedStreamTestBase.java @@ -26,6 +26,7 @@ import java.io.InputStream; import java.io.Reader; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; @@ -253,14 +254,14 @@ private static void close(Object obj) throws IOException { protected static String readFromStream(InputStream is) throws IOException { try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) { IOUtils.copyAndCloseInput(is, buf); - return new String(buf.toByteArray(), "UTF-8"); + return new String(buf.toByteArray(), StandardCharsets.UTF_8); } } protected static String readPartiallyFromStream(InputStream is, int len) throws IOException { try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) { IOUtils.copyAtLeast(is, buf, len); - return new String(buf.toByteArray(), "UTF-8"); + return new String(buf.toByteArray(), StandardCharsets.UTF_8); } } diff --git a/core/src/test/java/org/apache/cxf/staxutils/transform/OutTransformWriterTest.java b/core/src/test/java/org/apache/cxf/staxutils/transform/OutTransformWriterTest.java index 564f0f59f23..181f9c4b0d3 100644 --- a/core/src/test/java/org/apache/cxf/staxutils/transform/OutTransformWriterTest.java +++ b/core/src/test/java/org/apache/cxf/staxutils/transform/OutTransformWriterTest.java @@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -51,7 +52,7 @@ public class OutTransformWriterTest extends Assert { @Test public void testDefaultNamespace() throws Exception { ByteArrayOutputStream os = new ByteArrayOutputStream(); - XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(os, "UTF-8"); + XMLStreamWriter writer = StaxUtils.createXMLStreamWriter(os, StandardCharsets.UTF_8.name()); Map outMap = new HashMap(); outMap.put("{http://testbeans.com}*", "{http://testbeans.com/v2}*"); @@ -148,7 +149,7 @@ public void testReplaceSimpleElement() throws Exception { ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLStreamWriter writer = - new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, "UTF-8"), + new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, StandardCharsets.UTF_8.name()), null, Collections.singletonMap("{http://bar}a", "{http://bar}a=1 2 3"), null, null, false, null); StaxUtils.copy(new StreamSource(is), writer); @@ -255,7 +256,7 @@ public void testReadWithComplexTransformationNamespace2() throws Exception { // writing each child separately (as the soap header children are serialized) ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLStreamWriter writer = - new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, "UTF-8"), + new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, StandardCharsets.UTF_8.name()), map, null, null, null, false, null); boolean nsset = "ns3".equals(writer.getNamespaceContext().getPrefix("http://testbeans.com/double")); @@ -520,7 +521,7 @@ public void testReplaceDefaultNamespace() throws Exception { ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLStreamWriter writer = - new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, "UTF-8"), + new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, StandardCharsets.UTF_8.name()), null, null, null, null, false, ""); StaxUtils.copy(new StreamSource(is), writer); writer.flush(); diff --git a/core/src/test/java/org/apache/cxf/staxutils/transform/TransformTestUtils.java b/core/src/test/java/org/apache/cxf/staxutils/transform/TransformTestUtils.java index 89b8cc55357..df095bdf223 100755 --- a/core/src/test/java/org/apache/cxf/staxutils/transform/TransformTestUtils.java +++ b/core/src/test/java/org/apache/cxf/staxutils/transform/TransformTestUtils.java @@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -111,7 +112,7 @@ static XMLStreamReader createOutTransformedStreamReader( ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLStreamWriter writer = - new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, "UTF-8"), + new OutTransformWriter(StaxUtils.createXMLStreamWriter(os, StandardCharsets.UTF_8.name()), emap, append, dropEls, amap, attributesToElements, defaultNamespace); StaxUtils.copy(new StreamSource(TransformTestUtils.class.getResourceAsStream(file)), writer); writer.flush(); diff --git a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java index a17264f4e0d..a6c72d30473 100644 --- a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java +++ b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java @@ -68,7 +68,7 @@ public ModelAndView registerApp(@ModelAttribute("client") ClientApp clientApp) String consumerKey = clientApp.getConsumerKey(); if (StringUtils.isEmpty(consumerKey)) { consumerKey = tokenGen - .generate((principal.getName() + clientApp.getClientName()).getBytes("UTF-8")); + .generate((principal.getName() + clientApp.getClientName()).getBytes(StandardCharsets.UTF_8)); } String secretKey = tokenGen.generate(new SecureRandom().generateSeed(20)); diff --git a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java index e993cd74732..37424a03ac6 100644 --- a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java +++ b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java @@ -169,7 +169,7 @@ public void removeToken(Token t) { protected String generateToken() throws OAuthServiceException { String token; try { - token = tokenGenerator.generate(UUID.randomUUID().toString().getBytes("UTF-8")); + token = tokenGenerator.generate(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { throw new OAuthServiceException("Unable to create token ", e.getCause()); } diff --git a/rt/databinding/jaxb/src/main/java/org/apache/cxf/jaxb/io/DataWriterImpl.java b/rt/databinding/jaxb/src/main/java/org/apache/cxf/jaxb/io/DataWriterImpl.java index 8307f48dd25..2ec2558760e 100644 --- a/rt/databinding/jaxb/src/main/java/org/apache/cxf/jaxb/io/DataWriterImpl.java +++ b/rt/databinding/jaxb/src/main/java/org/apache/cxf/jaxb/io/DataWriterImpl.java @@ -23,6 +23,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; @@ -130,7 +131,7 @@ public Marshaller createMarshaller(Object elValue, MessagePartInfo part) { try { marshaller = context.createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + marshaller.setProperty(Marshaller.JAXB_ENCODING, StandardCharsets.UTF_8.name()); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); marshaller.setListener(databinding.getMarshallerListener()); diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java index aeb851dff92..d05d0d1545a 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java @@ -19,6 +19,7 @@ package org.apache.cxf.ext.logging; import java.io.IOException; +import java.nio.charset.StandardCharsets; import org.apache.cxf.common.injection.NoJSR250Annotations; import org.apache.cxf.common.util.StringUtils; @@ -64,7 +65,7 @@ public void handleMessage(Message message) throws Fault { private void handleOutputStream(final LogEvent event, Message message, CachedOutputStream cos) throws IOException { String encoding = (String)message.get(Message.ENCODING); if (StringUtils.isEmpty(encoding)) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); } StringBuilder payload = new StringBuilder(); cos.writeCacheTo(payload, encoding, limit); diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java index 80dbdecd72f..2dbe1502dcb 100644 --- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.ext.logging; import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -80,7 +81,7 @@ private void checkRequestOut(LogEvent requestOut) { Assert.assertEquals(SERVICE_URI, requestOut.getAddress()); Assert.assertEquals("text/xml", requestOut.getContentType()); Assert.assertEquals(EventType.REQ_OUT, requestOut.getType()); - Assert.assertEquals("UTF-8", requestOut.getEncoding()); + Assert.assertEquals(StandardCharsets.UTF_8.name(), requestOut.getEncoding()); Assert.assertNotNull(requestOut.getExchangeId()); Assert.assertEquals("POST", requestOut.getHttpMethod()); Assert.assertNotNull(requestOut.getMessageId()); @@ -94,7 +95,7 @@ private void checkRequestIn(LogEvent requestIn) { Assert.assertEquals(SERVICE_URI, requestIn.getAddress()); Assert.assertEquals("text/xml; charset=UTF-8", requestIn.getContentType()); Assert.assertEquals(EventType.REQ_IN, requestIn.getType()); - Assert.assertEquals("UTF-8", requestIn.getEncoding()); + Assert.assertEquals(StandardCharsets.UTF_8.name(), requestIn.getEncoding()); Assert.assertNotNull(requestIn.getExchangeId()); Assert.assertEquals("POST", requestIn.getHttpMethod()); Assert.assertNotNull(requestIn.getMessageId()); @@ -109,7 +110,7 @@ private void checkResponseOut(LogEvent responseOut) { Assert.assertNull(responseOut.getAddress()); Assert.assertEquals("text/xml", responseOut.getContentType()); Assert.assertEquals(EventType.RESP_OUT, responseOut.getType()); - Assert.assertEquals("UTF-8", responseOut.getEncoding()); + Assert.assertEquals(StandardCharsets.UTF_8.name(), responseOut.getEncoding()); Assert.assertNotNull(responseOut.getExchangeId()); // Not yet available @@ -125,7 +126,7 @@ private void checkResponseIn(LogEvent responseIn) { Assert.assertNull(responseIn.getAddress()); Assert.assertEquals("text/xml; charset=UTF-8", responseIn.getContentType()); Assert.assertEquals(EventType.RESP_IN, responseIn.getType()); - Assert.assertEquals("UTF-8", responseIn.getEncoding()); + Assert.assertEquals(StandardCharsets.UTF_8.name(), responseIn.getEncoding()); Assert.assertNotNull(responseIn.getExchangeId()); // Not yet available diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java index f172737fabc..12127b98263 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ResponseImpl.java @@ -26,6 +26,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -406,7 +407,7 @@ public InputStream convertEntityToStreamIfPossible() { } if (stringEntity != null) { try { - return new ByteArrayInputStream(stringEntity.getBytes("UTF-8")); + return new ByteArrayInputStream(stringEntity.getBytes(StandardCharsets.UTF_8)); } catch (Exception ex) { throw new ProcessingException(ex); } diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java index 5cafd8c770a..634d07a6509 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/interceptor/JAXRSOutInterceptor.java @@ -24,6 +24,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -394,7 +395,7 @@ private void writeResponseErrorMessage(Message message, OutputStream out, try { String errorMessage = JAXRSUtils.logMessageHandlerProblem(name, cls, ct); if (out != null) { - out.write(errorMessage.getBytes("UTF-8")); + out.write(errorMessage.getBytes(StandardCharsets.UTF_8)); } } catch (IOException another) { // ignore @@ -445,7 +446,7 @@ private boolean isResponseRedirected(Message m) { private void writeResponseToStream(OutputStream os, Object responseObj) { try { - byte[] bytes = responseObj.toString().getBytes("UTF-8"); + byte[] bytes = responseObj.toString().getBytes(StandardCharsets.UTF_8); os.write(bytes, 0, bytes.length); } catch (Exception ex) { LOG.severe("Problem with writing the data to the output stream"); diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java index b0cd13a1042..980b0765a50 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/BinaryDataProvider.java @@ -33,6 +33,7 @@ import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.security.DigestInputStream; import java.util.UUID; import java.util.logging.Logger; @@ -177,7 +178,7 @@ public void writeTo(T o, Class clazz, Type genericType, Annotation[] annotati private String getEncoding(MediaType mt) { String enc = mt.getParameters().get("charset"); - return enc == null ? "UTF-8" : enc; + return enc == null ? StandardCharsets.UTF_8.name() : enc; } private String getCharset(MediaType mt) { return mt.getParameters().get("charset"); diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/DataBindingProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/DataBindingProvider.java index 1a9532e5857..e5f5eed0786 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/DataBindingProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/DataBindingProvider.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; @@ -105,7 +106,7 @@ public void writeTo(T o, Class clazz, Type genericType, Annotation[] annotati throws IOException { XMLStreamWriter writer = null; try { - String enc = HttpUtils.getSetEncoding(m, headers, "UTF-8"); + String enc = HttpUtils.getSetEncoding(m, headers, StandardCharsets.UTF_8.name()); writer = createWriter(clazz, genericType, enc, os); writeToWriter(writer, o); } catch (Exception ex) { diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java index 1968763d751..3f78efe064b 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/FormEncodingProvider.java @@ -24,6 +24,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.ws.rs.Consumes; import javax.ws.rs.Encoded; @@ -168,7 +169,7 @@ protected void populateMap(MultivaluedMap params, FormUtils.populateMapFromMultipart(params, body, PhaseInterceptorChain.getCurrentMessage(), decode); } else { - String enc = HttpUtils.getEncoding(mt, "UTF-8"); + String enc = HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name()); Object servletRequest = mc != null ? mc.getHttpServletRequest() : null; if (servletRequest == null) { @@ -222,7 +223,7 @@ public void writeTo(T obj, Class c, Type t, Annotation[] anns, (MultivaluedMap)(obj instanceof Form ? ((Form)obj).asMap() : obj); boolean encoded = keepEncoded(anns); - String enc = HttpUtils.getSetEncoding(mt, headers, "UTF-8"); + String enc = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name()); FormUtils.writeMapToOutputStream(map, os, enc, encoded); diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java index 4c61df90cb6..8f834b5b06a 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/JAXBElementProvider.java @@ -26,6 +26,7 @@ import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -360,7 +361,7 @@ protected void marshalCollection(Class originalCls, Object collection, } StringBuilder pi = new StringBuilder(); - pi.append(XML_PI_START + (enc == null ? "UTF-8" : enc) + "\"?>"); + pi.append(XML_PI_START + (enc == null ? StandardCharsets.UTF_8.name() : enc) + "\"?>"); os.write(pi.toString().getBytes()); String startTag = null; String endTag = null; diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProvider.java index 31721b0cea8..a585afe702f 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProvider.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; @@ -53,7 +54,7 @@ public boolean isReadable(Class type, Type genericType, Annotation[] annotati public T readFrom(Class type, Type genType, Annotation[] anns, MediaType mt, MultivaluedMap headers, InputStream is) throws IOException { - String string = IOUtils.toString(is, HttpUtils.getEncoding(mt, "UTF-8")); + String string = IOUtils.toString(is, HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name())); if (StringUtils.isEmpty(string)) { reportEmptyContentLength(); } @@ -82,7 +83,7 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotat public void writeTo(T obj, Class type, Type genType, Annotation[] anns, MediaType mt, MultivaluedMap headers, OutputStream os) throws IOException { - String encoding = HttpUtils.getSetEncoding(mt, headers, "UTF-8"); + String encoding = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name()); byte[] bytes = obj.toString().getBytes(encoding); os.write(bytes); diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java index 654a9ffb046..52bf4951b8a 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/SourceProvider.java @@ -24,6 +24,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.logging.Logger; import javax.ws.rs.Consumes; @@ -186,7 +187,7 @@ public void writeTo(T source, Class clazz, Type genericType, Annotation[] ann MediaType mt, MultivaluedMap headers, OutputStream os) throws IOException { - String encoding = HttpUtils.getSetEncoding(mt, headers, "UTF-8"); + String encoding = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name()); XMLStreamReader reader = source instanceof Source ? StaxUtils.createXMLStreamReader((Source)source) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/StringTextProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/StringTextProvider.java index e6e25dab109..6d26a99eb95 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/StringTextProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/StringTextProvider.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; @@ -41,7 +42,7 @@ public boolean isReadable(Class type, Type genericType, Annotation[] annotati public String readFrom(Class type, Type genType, Annotation[] anns, MediaType mt, MultivaluedMap headers, InputStream is) throws IOException { - return IOUtils.toString(is, HttpUtils.getEncoding(mt, "UTF-8")); + return IOUtils.toString(is, HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name())); } public long getSize(String t, Class type, Type genericType, Annotation[] annotations, MediaType mt) { @@ -55,7 +56,7 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotat public void writeTo(String obj, Class type, Type genType, Annotation[] anns, MediaType mt, MultivaluedMap headers, OutputStream os) throws IOException { - String encoding = HttpUtils.getSetEncoding(mt, headers, "UTF-8"); + String encoding = HttpUtils.getSetEncoding(mt, headers, StandardCharsets.UTF_8.name()); //REVISIT try to avoid instantiating the whole byte array byte[] bytes = obj.getBytes(encoding); if (bytes.length > bufferSize) { diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java index bd33ad21434..dd929782ab3 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/XSLTJaxbProvider.java @@ -26,6 +26,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -515,7 +516,7 @@ protected Templates createTemplates(URL urlStream) { } Reader r = new BufferedReader( - new InputStreamReader(urlStream.openStream(), "UTF-8")); + new InputStreamReader(urlStream.openStream(), StandardCharsets.UTF_8)); Source source = new StreamSource(r); source.setSystemId(urlStream.toExternalForm()); if (factory == null) { diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java index e91e60abb89..666fa28bd42 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; @@ -63,10 +64,9 @@ private FormUtils() { } public static String formToString(Form form) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - try { - FormUtils.writeMapToOutputStream(form.asMap(), bos, "UTF-8", false); - return bos.toString("UTF-8"); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + FormUtils.writeMapToOutputStream(form.asMap(), bos, StandardCharsets.UTF_8.name(), false); + return bos.toString(StandardCharsets.UTF_8.name()); } catch (Exception ex) { // will not happen } diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java index 61664b4c553..e94594336f3 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java @@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -132,7 +133,7 @@ public static String queryEncode(String value) { public static String urlEncode(String value) { - return urlEncode(value, "UTF-8"); + return urlEncode(value, StandardCharsets.UTF_8.name()); } public static String urlEncode(String value, String enc) { @@ -558,7 +559,7 @@ public static String getSetEncoding(MediaType mt, MultivaluedMap headers.putSingle(HttpHeaders.CONTENT_TYPE, JAXRSUtils.mediaTypeToString(mt, CHARSET_PARAMETER) + ';' + CHARSET_PARAMETER + "=" - + (defaultEncoding == null ? "UTF-8" : defaultEncoding)); + + (defaultEncoding == null ? StandardCharsets.UTF_8 : defaultEncoding)); } return defaultEncoding; } diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java index ab2a55cb629..26120ec7552 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java @@ -27,6 +27,7 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -945,7 +946,7 @@ private static Object processFormParam(Message m, String key, m.put(FormUtils.FORM_PARAM_MAP, params); if (mt == null || mt.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE)) { - String enc = HttpUtils.getEncoding(mt, "UTF-8"); + String enc = HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name()); String body = FormUtils.readBody(m.getContent(InputStream.class), enc); HttpServletRequest request = (HttpServletRequest)m.get(AbstractHTTPDestination.HTTP_REQUEST); FormUtils.populateMapFromString(params, m, body, enc, decode, request); diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java index 4bbbea109ed..dc370eaa5a6 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ResourceUtils.java @@ -28,6 +28,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -542,7 +543,7 @@ public static List getUserResources(String loc) { } public static List getUserResources(InputStream is) throws Exception { - Document doc = StaxUtils.read(new InputStreamReader(is, "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(is, StandardCharsets.UTF_8)); return getResourcesFromElement(doc.getDocumentElement()); } diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java index f085535609f..5a972970232 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/schemas/SchemaHandler.java @@ -25,6 +25,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -101,7 +102,7 @@ public static Schema createSchema(List locations, String catalogLocation } for (URL schemaURL : schemaURLs) { Reader r = new BufferedReader( - new InputStreamReader(schemaURL.openStream(), "UTF-8")); + new InputStreamReader(schemaURL.openStream(), StandardCharsets.UTF_8)); StreamSource source = new StreamSource(r); source.setSystemId(schemaURL.toString()); sources.add(source); diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestImplTest.java index c38dbc727ed..0690e9d7c4f 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/RequestImplTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.jaxrs.impl; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -66,9 +67,9 @@ public void testSingleMatchingVariant() { metadata.putSingle(HttpHeaders.CONTENT_LANGUAGE, "en"); metadata.putSingle(HttpHeaders.CONTENT_ENCODING, "utf-8"); - assertSameVariant(MediaType.APPLICATION_XML_TYPE, new Locale("en"), "UTF-8"); - assertSameVariant(null, new Locale("en"), "UTF-8"); - assertSameVariant(MediaType.APPLICATION_XML_TYPE, null, "UTF-8"); + assertSameVariant(MediaType.APPLICATION_XML_TYPE, new Locale("en"), StandardCharsets.UTF_8.name()); + assertSameVariant(null, new Locale("en"), StandardCharsets.UTF_8.name()); + assertSameVariant(MediaType.APPLICATION_XML_TYPE, null, StandardCharsets.UTF_8.name()); assertSameVariant(MediaType.APPLICATION_XML_TYPE, new Locale("en"), null); } @@ -77,9 +78,9 @@ public void testSingleMatchingVariant() { public void testSingleMatchingVariantWithContentTypeOnly() { metadata.putSingle(HttpHeaders.CONTENT_TYPE, "application/xml"); - assertSameVariant(MediaType.APPLICATION_XML_TYPE, new Locale("en"), "UTF-8"); - assertSameVariant(null, new Locale("en"), "UTF-8"); - assertSameVariant(MediaType.APPLICATION_XML_TYPE, null, "UTF-8"); + assertSameVariant(MediaType.APPLICATION_XML_TYPE, new Locale("en"), StandardCharsets.UTF_8.name()); + assertSameVariant(null, new Locale("en"), StandardCharsets.UTF_8.name()); + assertSameVariant(MediaType.APPLICATION_XML_TYPE, null, StandardCharsets.UTF_8.name()); assertSameVariant(MediaType.APPLICATION_XML_TYPE, new Locale("en"), null); } diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java index 57bba2c2556..3409090f792 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/FormEncodingProviderTest.java @@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; import java.util.List; import javax.ws.rs.Consumes; @@ -279,7 +280,7 @@ public void testReadChineeseChars() throws Exception { (MultivaluedMap)ferp.readFrom(MultivaluedMap.class, null, new Annotation[]{}, MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED + ";charset=UTF-8"), null, - new ByteArrayInputStream(s.getBytes("UTF-8"))); + new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8))); String value = mvMap.getFirst("name"); assertEquals(s, "name=" + value); } diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/JAXBElementProviderTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/JAXBElementProviderTest.java index df4e9f391a8..b3b1350a4a7 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/JAXBElementProviderTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/JAXBElementProviderTest.java @@ -28,6 +28,7 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -130,7 +131,7 @@ public void testReadChineeseChars() throws Exception { Book book = p.readFrom(Book.class, null, new Annotation[]{}, MediaType.valueOf(MediaType.APPLICATION_XML + ";charset=UTF-8"), null, - new ByteArrayInputStream(bookStringUTF16.getBytes("UTF-8"))); + new ByteArrayInputStream(bookStringUTF16.getBytes(StandardCharsets.UTF_8))); assertEquals(book.getName(), nameStringUTF16); } @@ -705,7 +706,7 @@ public void testReadJAXBElement() throws Exception { JAXBElementProvider provider = new JAXBElementProvider(); JAXBElement jaxbElement = provider.readFrom(JAXBElement.class, Book.class, new Annotation[0], MediaType.TEXT_XML_TYPE, new MetadataMap(), - new ByteArrayInputStream(xml.getBytes("UTF-8"))); + new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); Book book = jaxbElement.getValue(); assertEquals(123L, book.getId()); assertEquals("CXF in Action", book.getName()); @@ -719,7 +720,7 @@ public void testReadParamJAXBElement() throws Exception { JAXBElementProvider provider = new JAXBElementProvider(); ParamJAXBElement jaxbElement = provider.readFrom(ParamJAXBElement.class, ParamJAXBElement.class, new Annotation[0], MediaType.TEXT_XML_TYPE, new MetadataMap(), - new ByteArrayInputStream(xml.getBytes("UTF-8"))); + new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); ParamType param = jaxbElement.getValue(); assertEquals("a", param.getComment()); } diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProviderTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProviderTest.java index 95fb77fdac4..0f500c1ef79 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProviderTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/provider/PrimitiveTextProviderTest.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import javax.ws.rs.core.MediaType; @@ -206,7 +207,7 @@ public void testReadChineeseChars() throws Exception { String value = (String)p.readFrom(String.class, null, new Annotation[]{}, MediaType.valueOf(MediaType.APPLICATION_XML + ";charset=UTF-8"), null, - new ByteArrayInputStream(s.getBytes("UTF-8"))); + new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8))); assertEquals(value, value); } } diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java index ac90b1e1777..ad78c24f6fa 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/FormUtilsTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.jaxrs.utils; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; @@ -54,7 +55,8 @@ public void populateMapFromStringFromHTTP() { EasyMock.replay(mockMessage, mockRequest); MultivaluedMap params = new MetadataMap(); - FormUtils.populateMapFromString(params, mockMessage, null, "UTF-8", false, mockRequest); + FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(), + false, mockRequest); assertEquals(2, params.size()); assertEquals(HTTP_PARAM_VALUE1, params.get(HTTP_PARAM1).iterator().next()); @@ -66,7 +68,8 @@ public void populateMapFromStringFromHTTPWithProp() { EasyMock.replay(mockMessage, mockRequest); MultivaluedMap params = new MetadataMap(); - FormUtils.populateMapFromString(params, mockMessage, null, "UTF-8", false, mockRequest); + FormUtils.populateMapFromString(params, mockMessage, null, StandardCharsets.UTF_8.name(), + false, mockRequest); assertEquals(0, params.size()); } @@ -78,7 +81,8 @@ public void populateMapFromStringFromBody() { MultivaluedMap params = new MetadataMap(); String postBody = FORM_PARAM1 + "=" + FORM_PARAM_VALUE1 + "&" + FORM_PARAM2 + "=" + FORM_PARAM_VALUE2; - FormUtils.populateMapFromString(params, mockMessage, postBody, "UTF-8", false, mockRequest); + FormUtils.populateMapFromString(params, mockMessage, postBody, StandardCharsets.UTF_8.name(), + false, mockRequest); assertEquals(2, params.size()); assertEquals(FORM_PARAM_VALUE1, params.get(FORM_PARAM1).iterator().next()); diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/JAXRSUtilsTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/JAXRSUtilsTest.java index aa051c69745..856d02f1df7 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/JAXRSUtilsTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/utils/JAXRSUtilsTest.java @@ -25,6 +25,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -121,8 +122,8 @@ public void tearDown() { @Test public void testFormParametersUTF8Encoding() throws Exception { JAXRSUtils.intersectMimeTypes("application/json", "application/json+v2"); - doTestFormParamsWithEncoding("UTF-8", true); - doTestFormParamsWithEncoding("UTF-8", false); + doTestFormParamsWithEncoding(StandardCharsets.UTF_8.name(), true); + doTestFormParamsWithEncoding(StandardCharsets.UTF_8.name(), false); } @Test diff --git a/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/SwAOutInterceptor.java b/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/SwAOutInterceptor.java index 55e0a26ea07..7eeb9c2489c 100644 --- a/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/SwAOutInterceptor.java +++ b/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/interceptors/SwAOutInterceptor.java @@ -25,6 +25,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; @@ -226,11 +227,7 @@ protected void processAttachments(SoapMessage message, SoapBodyInfo sbi) { if (ct == null) { ct = "text/plain; charset=\'UTF-8\'"; } - try { - dh = new DataHandler(new ByteDataSource(((String)o).getBytes("UTF-8"), ct)); - } catch (IOException e) { - throw new Fault(e); - } + dh = new DataHandler(new ByteDataSource(((String)o).getBytes(StandardCharsets.UTF_8), ct)); } else { throw new Fault(new org.apache.cxf.common.i18n.Message("ATTACHMENT_NOT_SUPPORTED", LOG, o.getClass())); @@ -260,7 +257,7 @@ private DataSource createDataSource(Source o, String ct) { IOUtils.copy(src.getInputStream(), bos, 1024); ds = new ByteDataSource(bos.toByteArray(), ct); } else { - ds = new ByteDataSource(IOUtils.toString(src.getReader()).getBytes("UTF-8"), + ds = new ByteDataSource(IOUtils.toString(src.getReader()).getBytes(StandardCharsets.UTF_8), ct); } } catch (IOException e) { diff --git a/rt/frontend/js/src/main/java/org/apache/cxf/js/rhino/JsServiceFactoryBean.java b/rt/frontend/js/src/main/java/org/apache/cxf/js/rhino/JsServiceFactoryBean.java index db079d782c9..b9221d65b85 100644 --- a/rt/frontend/js/src/main/java/org/apache/cxf/js/rhino/JsServiceFactoryBean.java +++ b/rt/frontend/js/src/main/java/org/apache/cxf/js/rhino/JsServiceFactoryBean.java @@ -21,6 +21,7 @@ import java.io.File; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; @@ -74,7 +75,7 @@ public String getJs() { public void create() throws Exception { BusFactory.setDefaultBus(bus); String jsFileString = getClass().getResource(js).toURI().getPath(); - jsFileString = URLDecoder.decode(jsFileString, "UTF-8"); + jsFileString = URLDecoder.decode(jsFileString, StandardCharsets.UTF_8.name()); File file = new File(jsFileString); providerFactory.createAndPublish(file, address, isBaseAddr); } diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisElementProvider.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisElementProvider.java index c8fc34e479b..e0aa712ea28 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisElementProvider.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisElementProvider.java @@ -26,6 +26,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; @@ -113,7 +114,7 @@ public void writeTo(T obj, Class type, Type genericType, Annotation[] anns, AegisType aegisType = context.getTypeMapping().getType(genericType); AegisWriter aegisWriter = context.createXMLStreamWriter(); try { - String enc = HttpUtils.getSetEncoding(m, headers, "UTF-8"); + String enc = HttpUtils.getSetEncoding(m, headers, StandardCharsets.UTF_8.name()); XMLStreamWriter xmlStreamWriter = createStreamWriter(aegisType.getSchemaType(), enc, os); // use type qname as element qname? xmlStreamWriter.writeStartDocument(); diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisJSONProvider.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisJSONProvider.java index 3bf58946c50..0a2591a8533 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisJSONProvider.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/aegis/AegisJSONProvider.java @@ -24,6 +24,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -114,7 +115,7 @@ public void writeTo(T obj, Class type, Type genericType, Annotation[] anns, M Document dom = w3cStreamWriter.getDocument(); // ok, now the namespace map has all the prefixes. - String enc = HttpUtils.getSetEncoding(m, headers, "UTF-8"); + String enc = HttpUtils.getSetEncoding(m, headers, StandardCharsets.UTF_8.name()); XMLStreamWriter xmlStreamWriter = createStreamWriter(aegisType.getSchemaType(), enc, os); xmlStreamWriter.writeStartDocument(); diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JSONProvider.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JSONProvider.java index 2a53be9b23d..24f694f37c8 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JSONProvider.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JSONProvider.java @@ -30,6 +30,7 @@ import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; @@ -216,7 +217,7 @@ public T readFrom(Class type, Type genericType, Annotation[] anns, MediaType } XMLStreamReader reader = null; - String enc = HttpUtils.getEncoding(mt, "UTF-8"); + String enc = HttpUtils.getEncoding(mt, StandardCharsets.UTF_8.name()); Unmarshaller unmarshaller = null; try { InputStream realStream = getInputStream(type, genericType, is); @@ -374,7 +375,7 @@ public void writeTo(T obj, Class cls, Type genericType, Annotation[] anns, XMLStreamWriter writer = null; try { - String enc = HttpUtils.getSetEncoding(m, headers, "UTF-8"); + String enc = HttpUtils.getSetEncoding(m, headers, StandardCharsets.UTF_8.name()); if (Document.class.isAssignableFrom(cls)) { writer = createWriter(obj, cls, genericType, enc, os, false); copyReaderToWriter(StaxUtils.createXMLStreamReader((Document)obj), writer); diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/utils/JSONUtils.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/utils/JSONUtils.java index 6c37267411b..1e998718531 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/utils/JSONUtils.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/utils/JSONUtils.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -135,7 +136,7 @@ private static String getKey(MappedNamespaceConvention convention, QName qname) public static XMLStreamReader createStreamReader(InputStream is, boolean readXsiType, ConcurrentHashMap namespaceMap) throws Exception { - return createStreamReader(is, readXsiType, namespaceMap, null, null, null, "UTF-8"); + return createStreamReader(is, readXsiType, namespaceMap, null, null, null, StandardCharsets.UTF_8.name()); } public static XMLStreamReader createStreamReader(InputStream is, boolean readXsiType, diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/AbstractJsonpOutInterceptor.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/AbstractJsonpOutInterceptor.java index 2760488c147..2c1a807ebca 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/AbstractJsonpOutInterceptor.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/AbstractJsonpOutInterceptor.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletResponse; @@ -42,7 +43,7 @@ protected String getCallbackValue(Message message) { protected void writeValue(Message message, String value) throws Fault { try { - getOutputStream(message).write(value.getBytes("UTF-8")); + getOutputStream(message).write(value.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new Fault(e); } diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/JsonpJaxrsWriterInterceptor.java b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/JsonpJaxrsWriterInterceptor.java index 2086d6c70a4..6322964766d 100644 --- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/JsonpJaxrsWriterInterceptor.java +++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/jsonp/JsonpJaxrsWriterInterceptor.java @@ -20,6 +20,7 @@ package org.apache.cxf.jaxrs.provider.jsonp; import java.io.IOException; +import java.nio.charset.StandardCharsets; import javax.ws.rs.WebApplicationException; import javax.ws.rs.ext.WriterInterceptor; @@ -47,7 +48,7 @@ public void aroundWriteTo(WriterInterceptorContext context) throws IOException, if (!StringUtils.isEmpty(callback)) { context.getHeaders().putSingle(Message.CONTENT_TYPE, JAXRSUtils.toMediaType(getMediaType())); - context.getOutputStream().write((callback + getPaddingEnd()).getBytes("UTF-8")); + context.getOutputStream().write((callback + getPaddingEnd()).getBytes(StandardCharsets.UTF_8)); } context.proceed(); } diff --git a/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/atom/AtomPojoProviderTest.java b/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/atom/AtomPojoProviderTest.java index 934f76375da..0296764b5d6 100644 --- a/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/atom/AtomPojoProviderTest.java +++ b/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/atom/AtomPojoProviderTest.java @@ -22,7 +22,7 @@ import java.io.ByteArrayOutputStream; import java.io.StringReader; import java.lang.annotation.Annotation; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -197,7 +197,7 @@ public void testReadEntryNoContent() throws Exception { new Annotation[0], MediaType.valueOf("application/atom+xml;type=entry"), new MetadataMap(), - new ByteArrayInputStream(entryNoContent.getBytes(Charset.forName("UTF-8")))); + new ByteArrayInputStream(entryNoContent.getBytes(StandardCharsets.UTF_8))); assertNull(type); } @@ -231,7 +231,7 @@ private void doReadEntryWithContent(String mediaType) throws Exception { new Annotation[0], MediaType.valueOf(mediaType), new MetadataMap(), - new ByteArrayInputStream(entryWithContent.getBytes(Charset.forName("UTF-8")))); + new ByteArrayInputStream(entryWithContent.getBytes(StandardCharsets.UTF_8))); assertNotNull(type); } diff --git a/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/dom4j/DOM4JProviderTest.java b/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/dom4j/DOM4JProviderTest.java index f4fa478f8af..a0912a01140 100644 --- a/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/dom4j/DOM4JProviderTest.java +++ b/rt/rs/extensions/providers/src/test/java/org/apache/cxf/jaxrs/provider/dom4j/DOM4JProviderTest.java @@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; @@ -58,7 +59,7 @@ private org.dom4j.Document readXML(MediaType ct, final String xml) throws Except p.setProviders(new ProvidersImpl(createMessage(false))); org.dom4j.Document dom = p.readFrom(org.dom4j.Document.class, org.dom4j.Document.class, new Annotation[] {}, ct, new MetadataMap(), - new ByteArrayInputStream(xml.getBytes("UTF-8"))); + new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); return dom; } @@ -69,7 +70,7 @@ public void testReadJSONConvertToXML() throws Exception { p.setProviders(new ProvidersImpl(createMessage(false))); org.dom4j.Document dom = p.readFrom(org.dom4j.Document.class, org.dom4j.Document.class, new Annotation[]{}, MediaType.APPLICATION_JSON_TYPE, new MetadataMap(), - new ByteArrayInputStream(xml.getBytes("UTF-8"))); + new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); String str = dom.asXML(); // starts with the xml PI assertTrue(str.contains("2")); diff --git a/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/hbase/HBaseQueryVisitor.java b/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/hbase/HBaseQueryVisitor.java index 8d466e4387f..453d6098c7b 100644 --- a/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/hbase/HBaseQueryVisitor.java +++ b/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/hbase/HBaseQueryVisitor.java @@ -18,7 +18,7 @@ */ package org.apache.cxf.jaxrs.ext.search.hbase; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -118,10 +118,10 @@ private Filter buildSimpleQuery(ConditionType ct, String name, Object value) { String theFamily = family != null ? family : familyMap.get(qualifier); ByteArrayComparable byteArrayComparable = regexCompRequired ? new RegexStringComparator(value.toString().replace("*", ".")) - : new BinaryComparator(getBytes(value.toString())); + : new BinaryComparator(value.toString().getBytes(StandardCharsets.UTF_8)); - Filter query = new SingleColumnValueFilter(getBytes(theFamily), - getBytes(qualifier), + Filter query = new SingleColumnValueFilter(theFamily.getBytes(StandardCharsets.UTF_8), + qualifier.getBytes(StandardCharsets.UTF_8), compareOp, byteArrayComparable); return query; @@ -138,11 +138,4 @@ private Filter createCompositeQuery(List queries, boolean orCondition) { return list; } - private byte[] getBytes(String value) { - try { - return value.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } } diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java index 83e3533b2ed..d28b1b7dd20 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.rs.security.jose.common.JoseUtils; @@ -33,7 +34,8 @@ public class AbstractJweDecryptingFilter { private JweDecryptionProvider decryption; private String defaultMediaType; protected JweDecryptionOutput decrypt(InputStream is) throws IOException { - JweCompactConsumer jwe = new JweCompactConsumer(new String(IOUtils.readBytesFromStream(is), "UTF-8")); + JweCompactConsumer jwe = new JweCompactConsumer(new String(IOUtils.readBytesFromStream(is), + StandardCharsets.UTF_8)); JweDecryptionProvider theDecryptor = getInitializedDecryptionProvider(jwe.getJweHeaders()); JweDecryptionOutput out = new JweDecryptionOutput(jwe.getJweHeaders(), jwe.getDecryptedContent(theDecryptor)); JoseUtils.traceHeaders(out.getHeaders()); diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java index 9522b9be1c9..dc99b7cc6ce 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsJsonWriterInterceptor.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -89,7 +90,7 @@ public void aroundWriteTo(WriterInterceptorContext ctx) throws IOException, WebA CachedOutputStream cos = new CachedOutputStream(); ctx.setOutputStream(cos); ctx.proceed(); - JwsJsonProducer p = new JwsJsonProducer(new String(cos.getBytes(), "UTF-8")); + JwsJsonProducer p = new JwsJsonProducer(new String(cos.getBytes(), StandardCharsets.UTF_8)); for (JwsSignatureProvider signer : sigProviders) { JwsHeaders protectedHeader = prepareProtectedHeader(ctx, signer); p.signWith(signer, protectedHeader, null); diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java index 4657c9e820a..e4d8affb0ac 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import javax.annotation.Priority; import javax.ws.rs.WebApplicationException; @@ -75,7 +76,7 @@ public void aroundWriteTo(WriterInterceptorContext ctx) throws IOException, WebA CachedOutputStream cos = new CachedOutputStream(); ctx.setOutputStream(cos); ctx.proceed(); - JwsCompactProducer p = new JwsCompactProducer(headers, new String(cos.getBytes(), "UTF-8")); + JwsCompactProducer p = new JwsCompactProducer(headers, new String(cos.getBytes(), StandardCharsets.UTF_8)); setJoseMediaType(ctx); writeJws(p, sigProvider, actualOs); } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java index 46495173377..225540ed5e4 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseUtils.java @@ -20,8 +20,8 @@ import java.io.File; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; import java.util.Properties; @@ -110,12 +110,7 @@ public static String expandContentType(String contentType) { } public static String decodeToString(String encoded) { - try { - return new String(decode(encoded), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new JoseException(ex); - } - + return new String(decode(encoded), StandardCharsets.UTF_8); } public static byte[] decode(String encoded) { return CryptoUtils.decodeSequence(encoded); diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweCompactConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweCompactConsumer.java index eb0c2a70df4..d508e36dd5e 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweCompactConsumer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweCompactConsumer.java @@ -19,7 +19,7 @@ package org.apache.cxf.rs.security.jose.jwe; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.logging.Logger; import org.apache.cxf.common.logging.LogUtils; @@ -27,7 +27,6 @@ import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.jaxrs.json.basic.JsonMapObject; import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; -import org.apache.cxf.rs.security.jose.common.JoseException; import org.apache.cxf.rs.security.jose.common.JoseUtils; @@ -113,11 +112,7 @@ public byte[] getDecryptedContent(JweDecryptionProvider decryption) { return decryption.decrypt(jweDecryptionInput); } public String getDecryptedContentText(JweDecryptionProvider decryption) { - try { - return new String(getDecryptedContent(decryption), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new JoseException(ex); - } + return new String(getDecryptedContent(decryption), StandardCharsets.UTF_8); } public boolean validateCriticalHeaders() { return JweUtils.validateCriticalHeaders(getJweHeaders()); diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweDecryptionOutput.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweDecryptionOutput.java index 34322369d3b..bb6842426b7 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweDecryptionOutput.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweDecryptionOutput.java @@ -18,9 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jwe; -import java.io.UnsupportedEncodingException; - -import org.apache.cxf.rs.security.jose.common.JoseException; +import java.nio.charset.StandardCharsets; public class JweDecryptionOutput { private JweHeaders headers; @@ -36,10 +34,6 @@ public byte[] getContent() { return content; } public String getContentText() { - try { - return new String(getContent(), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new JoseException(ex); - } + return new String(getContent(), StandardCharsets.UTF_8); } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumer.java index 35352089159..4c2a694318d 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumer.java @@ -18,7 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jwe; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -29,7 +29,6 @@ import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; -import org.apache.cxf.rs.security.jose.common.JoseException; import org.apache.cxf.rs.security.jose.common.JoseUtils; import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; @@ -175,11 +174,7 @@ public String getAadText() { if (aad == null) { return null; } - try { - return new String(aad, "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new JoseException(ex); - } + return new String(aad, StandardCharsets.UTF_8); } public List getRecipients() { return recipients; diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJwtCompactConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJwtCompactConsumer.java index 247f84b67b1..451b464f365 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJwtCompactConsumer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJwtCompactConsumer.java @@ -17,6 +17,7 @@ * under the License. */ package org.apache.cxf.rs.security.jose.jwe; +import java.nio.charset.StandardCharsets; import java.security.interfaces.RSAPrivateKey; import javax.crypto.SecretKey; @@ -59,7 +60,7 @@ public JweHeaders getHeaders() { } private static String toString(byte[] bytes) { try { - return new String(bytes, "UTF-8"); + return new String(bytes, StandardCharsets.UTF_8); } catch (Exception ex) { throw new RuntimeException(ex); } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java index 0a17be543da..b52b9bd2471 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java @@ -20,7 +20,7 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; @@ -179,7 +179,7 @@ static byte[] stringToBytes(String str) { return StringUtils.toBytesUTF8(str); } static byte[] charsToBytes(char[] chars) { - ByteBuffer bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(chars)); + ByteBuffer bb = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chars)); byte[] b = new byte[bb.remaining()]; bb.get(b); return b; diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index cd609f54fd9..3fca28d5014 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.math.BigInteger; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; @@ -501,7 +502,7 @@ private static JsonWebKey prepareRSAJwk(BigInteger modulus, String algo) { } private static String toString(byte[] bytes) { try { - return new String(bytes, "UTF-8"); + return new String(bytes, StandardCharsets.UTF_8); } catch (Exception ex) { throw new RuntimeException(ex); } diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java index c93fb0e2168..0188d8edc6d 100644 --- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jwe; +import java.nio.charset.StandardCharsets; import java.security.Security; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; @@ -107,7 +108,7 @@ public void testEncryptDecryptAesWrapA128CBCHS256() throws Exception { CONTENT_ENCRYPTION_KEY_A3, INIT_VECTOR_A3, keyEncryption); - String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + String jweContent = encryption.encrypt(specPlainText.getBytes(StandardCharsets.UTF_8), null); assertEquals(JWE_OUTPUT_A3, jweContent); AesWrapKeyDecryptionAlgorithm keyDecryption = new AesWrapKeyDecryptionAlgorithm(cekEncryptionKey); @@ -152,7 +153,7 @@ public void testEncryptDecryptRSA15WrapA128CBCHS256() throws Exception { CONTENT_ENCRYPTION_KEY_A3, INIT_VECTOR_A3, keyEncryption); - String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + String jweContent = encryption.encrypt(specPlainText.getBytes(StandardCharsets.UTF_8), null); RSAPrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED_A1, RSA_PRIVATE_EXPONENT_ENCODED_A1); @@ -180,7 +181,7 @@ public void testEncryptDecryptAesGcmWrapA128CBCHS256() throws Exception { CONTENT_ENCRYPTION_KEY_A3, INIT_VECTOR_A3, keyEncryption); - String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + String jweContent = encryption.encrypt(specPlainText.getBytes(StandardCharsets.UTF_8), null); AesGcmWrapKeyDecryptionAlgorithm keyDecryption = new AesGcmWrapKeyDecryptionAlgorithm(cekEncryptionKey); JweDecryptionProvider decryption = new AesCbcHmacJweDecryption(keyDecryption); @@ -228,12 +229,12 @@ private String encryptContent(String content, boolean createIfException) throws new AesGcmContentEncryptionAlgorithm(key == null ? null : key.getEncoded(), INIT_VECTOR_A1, ContentAlgorithm.getAlgorithm(jwtKeyName)); JweEncryptionProvider encryptor = new JweEncryption(keyEncryptionAlgo, contentEncryptionAlgo); - return encryptor.encrypt(content.getBytes("UTF-8"), null); + return encryptor.encrypt(content.getBytes(StandardCharsets.UTF_8), null); } private String encryptContentDirect(SecretKey key, String content) throws Exception { JweEncryption encryptor = new JweEncryption(new DirectKeyEncryptionAlgorithm(), new AesGcmContentEncryptionAlgorithm(key, INIT_VECTOR_A1, ContentAlgorithm.A128GCM)); - return encryptor.encrypt(content.getBytes("UTF-8"), null); + return encryptor.encrypt(content.getBytes(StandardCharsets.UTF_8), null); } private void decrypt(String jweContent, String plainContent, boolean unwrap) throws Exception { RSAPrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED_A1, diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java index 8898bf0b5df..ab00e97cf18 100644 --- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jwe; +import java.nio.charset.StandardCharsets; import java.security.Security; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; @@ -46,7 +47,7 @@ public void testEncryptDecryptPbesHmacAesWrapA128CBCHS256() throws Exception { new PbesHmacAesWrapKeyEncryptionAlgorithm(password, KeyAlgorithm.PBES2_HS256_A128KW); JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(ContentAlgorithm.A128CBC_HS256, keyEncryption); - String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + String jweContent = encryption.encrypt(specPlainText.getBytes(StandardCharsets.UTF_8), null); PbesHmacAesWrapKeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); JweDecryptionProvider decryption = new AesCbcHmacJweDecryption(keyDecryption); @@ -65,7 +66,7 @@ public void testEncryptDecryptPbesHmacAesWrapAesGcm() throws Exception { new PbesHmacAesWrapKeyEncryptionAlgorithm(password, KeyAlgorithm.PBES2_HS256_A128KW); JweEncryptionProvider encryption = new JweEncryption(keyEncryption, new AesGcmContentEncryptionAlgorithm(ContentAlgorithm.A128GCM)); - String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + String jweContent = encryption.encrypt(specPlainText.getBytes(StandardCharsets.UTF_8), null); PbesHmacAesWrapKeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); JweDecryptionProvider decryption = new JweDecryption(keyDecryption, new AesGcmContentDecryptionAlgorithm(ContentAlgorithm.A128GCM)); diff --git a/rt/rs/security/oauth-parent/oauth/src/main/java/org/apache/cxf/rs/security/oauth/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth/src/main/java/org/apache/cxf/rs/security/oauth/utils/OAuthUtils.java index 708144e8212..aa8f820406c 100644 --- a/rt/rs/security/oauth-parent/oauth/src/main/java/org/apache/cxf/rs/security/oauth/utils/OAuthUtils.java +++ b/rt/rs/security/oauth-parent/oauth/src/main/java/org/apache/cxf/rs/security/oauth/utils/OAuthUtils.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -130,7 +131,7 @@ public static void addParametersIfNeeded(MessageContext mc, OAuthMessage oAuthMessage) throws IOException { List> params = oAuthMessage.getParameters(); String enc = oAuthMessage.getBodyEncoding(); - enc = enc == null ? "UTF-8" : enc; + enc = enc == null ? StandardCharsets.UTF_8.name() : enc; if (params.isEmpty() && MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible( diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java index f8597c43666..71b876a9424 100644 --- a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.security.Principal; import java.security.cert.Certificate; import java.util.ArrayList; @@ -81,7 +82,8 @@ public class Saml2BearerGrantHandler extends AbstractGrantHandler { // AccessTokenService may be configured with the form provider // which will not decode by default - so listing both the actual // and encoded grant type value will help - ENCODED_SAML2_BEARER_GRANT = HttpUtils.urlEncode(Constants.SAML2_BEARER_GRANT, "UTF-8"); + ENCODED_SAML2_BEARER_GRANT = HttpUtils.urlEncode(Constants.SAML2_BEARER_GRANT, + StandardCharsets.UTF_8.name()); } private Validator samlValidator = new SamlAssertionValidator(); private SamlOAuthValidator samlOAuthValidator = new SamlOAuthValidator(); @@ -164,7 +166,7 @@ private InputStream decodeAssertion(String assertion) { protected Element readToken(InputStream tokenStream) { try { - Document doc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8)); return doc.getDocumentElement(); } catch (Exception ex) { throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index ae9f942bc8e..3e312a36ebb 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.List; import javax.annotation.Priority; @@ -213,9 +214,9 @@ protected MultivaluedMap toRequestState(ContainerRequestContext MultivaluedMap requestState = new MetadataMap(); requestState.putAll(ui.getQueryParameters(decodeRequestParameters)); if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) { - String body = FormUtils.readBody(rc.getEntityStream(), "UTF-8"); + String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name()); FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body, - "UTF-8", decodeRequestParameters); + StandardCharsets.UTF_8.name(), decodeRequestParameters); } return requestState; } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java index 4ee712cff34..971b4811fcf 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Map; @@ -285,7 +286,7 @@ public static ClientAccessToken getAccessToken(WebClient accessTokenService, sb.append("Basic "); try { String data = consumer.getKey() + ":" + consumer.getSecret(); - sb.append(Base64Utility.encode(data.getBytes("UTF-8"))); + sb.append(Base64Utility.encode(data.getBytes(StandardCharsets.UTF_8))); } catch (Exception ex) { throw new ProcessingException(ex); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerGrantHandler.java index 5bef10330e1..674e4f88b1b 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerGrantHandler.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.rs.security.oauth2.grants.jwt; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import javax.ws.rs.core.MultivaluedMap; @@ -42,7 +43,7 @@ public class JwtBearerGrantHandler extends AbstractJwtHandler { // AccessTokenService may be configured with the form provider // which will not decode by default - so listing both the actual // and encoded grant type value will help - ENCODED_JWT_BEARER_GRANT = HttpUtils.urlEncode(Constants.JWT_BEARER_GRANT, "UTF-8"); + ENCODED_JWT_BEARER_GRANT = HttpUtils.urlEncode(Constants.JWT_BEARER_GRANT, StandardCharsets.UTF_8.name()); } public JwtBearerGrantHandler() { super(Arrays.asList(Constants.JWT_BEARER_GRANT, ENCODED_JWT_BEARER_GRANT)); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJSONProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJSONProvider.java index fba68a9a68f..c850cefd923 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJSONProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJSONProvider.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -81,7 +82,7 @@ private void writeOAuthError(OAuthError obj, OutputStream os) throws IOException sb.append("}"); String result = sb.toString(); - os.write(result.getBytes("UTF-8")); + os.write(result.getBytes(StandardCharsets.UTF_8)); os.flush(); } @@ -110,7 +111,7 @@ private void writeAccessToken(ClientAccessToken obj, OutputStream os) throws IOE } sb.append("}"); String result = sb.toString(); - os.write(result.getBytes("UTF-8")); + os.write(result.getBytes(StandardCharsets.UTF_8)); os.flush(); } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java index 0cc0db4c4a4..7026c9cdb17 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcIdTokenRequestFilter.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.oidc.rp; import java.io.IOException; +import java.nio.charset.StandardCharsets; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; @@ -54,9 +55,9 @@ public void filter(ContainerRequestContext requestContext) throws IOException { private MultivaluedMap toFormData(ContainerRequestContext rc) { MultivaluedMap requestState = new MetadataMap(); if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) { - String body = FormUtils.readBody(rc.getEntityStream(), "UTF-8"); + String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name()); FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body, - "UTF-8", false); + StandardCharsets.UTF_8.name(), false); } return requestState; } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java index 415e2ccec68..de4cad0ce49 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.oidc.rp; import java.net.URI; +import java.nio.charset.StandardCharsets; import javax.annotation.Priority; import javax.ws.rs.Priorities; @@ -85,9 +86,9 @@ private MultivaluedMap toRequestState(ContainerRequestContext rc MultivaluedMap requestState = new MetadataMap(); requestState.putAll(rc.getUriInfo().getQueryParameters(true)); if (MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(rc.getMediaType())) { - String body = FormUtils.readBody(rc.getEntityStream(), "UTF-8"); + String body = FormUtils.readBody(rc.getEntityStream(), StandardCharsets.UTF_8.name()); FormUtils.populateMapFromString(requestState, JAXRSUtils.getCurrentMessage(), body, - "UTF-8", true); + StandardCharsets.UTF_8.name(), true); } return requestState; } diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java index 4d9638bfd29..2de61ae2d6a 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractRequestAssertionConsumerHandler.java @@ -22,8 +22,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.ResourceBundle; import java.util.UUID; @@ -235,7 +235,7 @@ private org.opensaml.saml.saml2.core.Response readSAMLResponse( // URL Decoding only applies for the re-direct binding if (!postBinding) { try { - samlResponseDecoded = URLDecoder.decode(samlResponse, "UTF-8"); + samlResponseDecoded = URLDecoder.decode(samlResponse, StandardCharsets.UTF_8); } catch (UnsupportedEncodingException e) { throw ExceptionUtils.toBadRequestException(null, null); } @@ -254,16 +254,12 @@ private org.opensaml.saml.saml2.core.Response readSAMLResponse( throw ExceptionUtils.toBadRequestException(ex, null); } } else { - try { - tokenStream = new ByteArrayInputStream(samlResponseDecoded.getBytes("UTF-8")); - } catch (UnsupportedEncodingException ex) { - throw ExceptionUtils.toBadRequestException(ex, null); - } + tokenStream = new ByteArrayInputStream(samlResponseDecoded.getBytes(StandardCharsets.UTF_8)); } Document responseDoc = null; try { - responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8)); } catch (Exception ex) { throw new WebApplicationException(400); } diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java index fe9ef867a5d..3d88dc8fb38 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java @@ -22,6 +22,7 @@ import java.io.StringReader; import java.net.URI; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.Map; import java.util.ResourceBundle; @@ -260,7 +261,7 @@ m, getIssuerId(m), getAbsoluteAssertionServiceAddress(m) getWebAppDomain(), System.currentTimeMillis()); - String relayState = URLEncoder.encode(UUID.randomUUID().toString(), "UTF-8"); + String relayState = URLEncoder.encode(UUID.randomUUID().toString(), StandardCharsets.UTF_8.name()); getStateProvider().setRequestState(relayState, requestState); info.setRelayState(relayState); info.setWebAppContext(webAppContext); diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/MetadataWriter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/MetadataWriter.java index 8363199a65f..d85b9e5e38a 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/MetadataWriter.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/MetadataWriter.java @@ -20,6 +20,7 @@ package org.apache.cxf.rs.security.saml.sso; import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -71,7 +72,7 @@ public Document getMetaData( W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); - writer.writeStartDocument("UTF-8", "1.0"); + writer.writeStartDocument(StandardCharsets.UTF_8.name(), "1.0"); String referenceID = IDGenerator.generateID("_"); writer.writeStartElement("md", "EntityDescriptor", SSOConstants.SAML2_METADATA_NS); diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java index abc949afa0c..d085a6e12f4 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLProtocolResponseValidator.java @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.logging.Level; @@ -481,7 +482,7 @@ private Element decryptAssertion( Document payloadDoc = null; try { payloadDoc = StaxUtils.read(new InputStreamReader(new ByteArrayInputStream(decryptedPayload), - "UTF-8")); + StandardCharsets.UTF_8)); return payloadDoc.getDocumentElement(); } catch (Exception ex) { LOG.log(Level.FINE, "Payload document can not be created", ex); diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlPostBindingFilter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlPostBindingFilter.java index 3f2f09f8407..9f2fe743c36 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlPostBindingFilter.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlPostBindingFilter.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.saml.sso; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -95,9 +96,9 @@ protected String encodeAuthnRequest(Element authnRequest) throws IOException { // Not correct according to the spec but required by some IDPs. if (useDeflateEncoding) { DeflateEncoderDecoder encoder = new DeflateEncoderDecoder(); - deflatedBytes = encoder.deflateToken(requestMessage.getBytes("UTF-8")); + deflatedBytes = encoder.deflateToken(requestMessage.getBytes(StandardCharsets.UTF_8)); } else { - deflatedBytes = requestMessage.getBytes("UTF-8"); + deflatedBytes = requestMessage.getBytes(StandardCharsets.UTF_8); } return Base64Utility.encode(deflatedBytes); diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java index 1b0ed7a9f5f..ebfc7b881fb 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SamlRedirectBindingFilter.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.X509Certificate; @@ -54,7 +55,7 @@ public void filter(ContainerRequestContext context) { try { SamlRequestInfo info = createSamlRequestInfo(m); String urlEncodedRequest = - URLEncoder.encode(info.getSamlRequest(), "UTF-8"); + URLEncoder.encode(info.getSamlRequest(), StandardCharsets.UTF_8.name()); UriBuilder ub = UriBuilder.fromUri(getIdpServiceAddress()); @@ -89,7 +90,7 @@ protected String encodeAuthnRequest(Element authnRequest) throws IOException { String requestMessage = DOM2Writer.nodeToString(authnRequest); DeflateEncoderDecoder encoder = new DeflateEncoderDecoder(); - byte[] deflatedBytes = encoder.deflateToken(requestMessage.getBytes("UTF-8")); + byte[] deflatedBytes = encoder.deflateToken(requestMessage.getBytes(StandardCharsets.UTF_8)); return Base64Utility.encode(deflatedBytes); } @@ -136,7 +137,7 @@ private void signRequest( jceSigAlgo = "SHA1withDSA"; } LOG.fine("Using Signature algorithm " + sigAlgo); - ub.queryParam(SSOConstants.SIG_ALG, URLEncoder.encode(sigAlgo, "UTF-8")); + ub.queryParam(SSOConstants.SIG_ALG, URLEncoder.encode(sigAlgo, StandardCharsets.UTF_8.name())); // Get the password WSPasswordCallback[] cb = {new WSPasswordCallback(signatureUser, WSPasswordCallback.SIGNATURE)}; @@ -153,14 +154,14 @@ private void signRequest( String requestToSign = SSOConstants.SAML_REQUEST + "=" + authnRequest + "&" + SSOConstants.RELAY_STATE + "=" + relayState + "&" - + SSOConstants.SIG_ALG + "=" + URLEncoder.encode(sigAlgo, "UTF-8"); + + SSOConstants.SIG_ALG + "=" + URLEncoder.encode(sigAlgo, StandardCharsets.UTF_8.name()); - signature.update(requestToSign.getBytes("UTF-8")); + signature.update(requestToSign.getBytes(StandardCharsets.UTF_8)); byte[] signBytes = signature.sign(); String encodedSignature = Base64.encode(signBytes); - ub.queryParam(SSOConstants.SIGNATURE, URLEncoder.encode(encodedSignature, "UTF-8")); + ub.queryParam(SSOConstants.SIGNATURE, URLEncoder.encode(encodedSignature, StandardCharsets.UTF_8.name())); } diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java index a124d7f84c9..c9f0cbf5f36 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -101,7 +102,7 @@ protected void validateToken(Message message, InputStream tokenStream) { protected Element readToken(Message message, InputStream tokenStream) { try { - Document doc = StaxUtils.read(new InputStreamReader(tokenStream, "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8)); return doc.getDocumentElement(); } catch (Exception ex) { throwFault("Assertion can not be read as XML document", ex); diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlOutInterceptor.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlOutInterceptor.java index 04a103288f3..663208619a0 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlOutInterceptor.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlOutInterceptor.java @@ -19,7 +19,7 @@ package org.apache.cxf.rs.security.saml; import java.io.StringWriter; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import org.apache.cxf.common.util.Base64Exception; import org.apache.cxf.common.util.Base64Utility; @@ -51,12 +51,8 @@ protected SamlAssertionWrapper createAssertion(Message message) throws Fault { } protected String encodeToken(String assertion) throws Base64Exception { - byte[] tokenBytes = null; - try { - tokenBytes = assertion.getBytes("UTF-8"); - } catch (UnsupportedEncodingException ex) { - // won't happen - } + byte[] tokenBytes = assertion.getBytes(StandardCharsets.UTF_8); + if (useDeflateEncoding) { tokenBytes = new DeflateEncoderDecoder().deflateToken(tokenBytes); } diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java index 01ef19f3f25..ee10d35d64d 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedInHandler.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import javax.ws.rs.HttpMethod; import javax.ws.rs.container.ContainerRequestContext; @@ -58,7 +59,7 @@ public void filter(ContainerRequestContext context) { InputStream is = message.getContent(InputStream.class); if (is != null) { try { - doc = StaxUtils.read(new InputStreamReader(is, "UTF-8")); + doc = StaxUtils.read(new InputStreamReader(is, StandardCharsets.UTF_8)); } catch (Exception ex) { throwFault("Invalid XML payload", ex); } diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlEncInHandler.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlEncInHandler.java index 680e46df15f..5cd76037dc6 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlEncInHandler.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlEncInHandler.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -82,7 +83,7 @@ public void decryptContent(Message message) { Document payloadDoc = null; try { payloadDoc = StaxUtils.read(new InputStreamReader(new ByteArrayInputStream(decryptedPayload), - "UTF-8")); + StandardCharsets.UTF_8)); } catch (Exception ex) { throwFault("Payload document can not be created", ex); } diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlSecInHandler.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlSecInHandler.java index f65368eea7b..8d79b1c3450 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlSecInHandler.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/AbstractXmlSecInHandler.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.logging.Logger; import javax.ws.rs.core.Response; @@ -78,7 +79,7 @@ protected Document getDocument(Message message) { InputStream is = message.getContent(InputStream.class); if (is != null) { try { - doc = StaxUtils.read(new InputStreamReader(is, "UTF-8")); + doc = StaxUtils.read(new InputStreamReader(is, StandardCharsets.UTF_8)); } catch (Exception ex) { throwFault("Invalid XML payload", ex); } diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java index bec7fce2e33..7b7d982cf3c 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.xml; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -418,7 +419,7 @@ private String getEncoding(Message message) { } if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); message.put(Message.ENCODING, encoding); } return encoding; diff --git a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java index 0c80fb49676..df3b19cae0f 100644 --- a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java +++ b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; @@ -409,7 +410,7 @@ public static String decryptSequence(String encodedData, Key secretKey, byte[] encryptedBytes = decodeSequence(encodedData); byte[] bytes = decryptBytes(encryptedBytes, secretKey, props); try { - return new String(bytes, "UTF-8"); + return new String(bytes, StandardCharsets.UTF_8); } catch (Exception ex) { throw new SecurityException(ex); } @@ -422,7 +423,7 @@ public static String encryptSequence(String sequence, Key secretKey) throws Secu public static String encryptSequence(String sequence, Key secretKey, KeyProperties keyProps) throws SecurityException { try { - byte[] bytes = encryptBytes(sequence.getBytes("UTF-8"), secretKey, keyProps); + byte[] bytes = encryptBytes(sequence.getBytes(StandardCharsets.UTF_8), secretKey, keyProps); return encodeBytes(bytes); } catch (Exception ex) { throw new SecurityException(ex); diff --git a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/HmacUtils.java b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/HmacUtils.java index bf502249d49..c40fe85bc0f 100644 --- a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/HmacUtils.java +++ b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/HmacUtils.java @@ -18,7 +18,7 @@ */ package org.apache.cxf.rt.security.crypto; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; @@ -86,11 +86,7 @@ public static byte[] computeHmac(byte[] key, String macAlgoJavaName, AlgorithmPa } public static byte[] computeHmac(String key, Mac hmac, String data) { - try { - return computeHmac(key.getBytes("UTF-8"), hmac, data); - } catch (UnsupportedEncodingException e) { - throw new SecurityException(e); - } + return computeHmac(key.getBytes(StandardCharsets.UTF_8), hmac, data); } public static byte[] computeHmac(byte[] key, Mac hmac, String data) { diff --git a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/MessageDigestUtils.java b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/MessageDigestUtils.java index 1c18df2c46f..28b85972987 100644 --- a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/MessageDigestUtils.java +++ b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/MessageDigestUtils.java @@ -18,7 +18,7 @@ */ package org.apache.cxf.rt.security.crypto; -import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -53,9 +53,7 @@ public static String generate(byte[] input, String algo) { public static byte[] createDigest(String input, String algo) { try { - return createDigest(input.getBytes("UTF-8"), algo); - } catch (UnsupportedEncodingException e) { - throw new SecurityException(e); + return createDigest(input.getBytes(StandardCharsets.UTF_8), algo); } catch (NoSuchAlgorithmException e) { throw new SecurityException(e); } diff --git a/rt/transports/http-jetty/src/test/java/org/apache/cxf/transport/http_jetty/JettyHTTPDestinationTest.java b/rt/transports/http-jetty/src/test/java/org/apache/cxf/transport/http_jetty/JettyHTTPDestinationTest.java index 4692c3aa069..e10fb2aa529 100644 --- a/rt/transports/http-jetty/src/test/java/org/apache/cxf/transport/http_jetty/JettyHTTPDestinationTest.java +++ b/rt/transports/http-jetty/src/test/java/org/apache/cxf/transport/http_jetty/JettyHTTPDestinationTest.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.OutputStream; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -659,7 +660,7 @@ private void setUpDoService(boolean setRedirectURL, EasyMock.expect(request.getRequestURI()).andReturn("/foo"); EasyMock.expect(request.getRequestURL()) .andReturn(new StringBuffer("http://localhost/foo")).anyTimes(); - EasyMock.expect(request.getCharacterEncoding()).andReturn("UTF-8"); + EasyMock.expect(request.getCharacterEncoding()).andReturn(StandardCharsets.UTF_8.name()); EasyMock.expect(request.getQueryString()).andReturn(query); EasyMock.expect(request.getHeader("Accept")).andReturn("*/*"); EasyMock.expect(request.getContentType()).andReturn("text/xml charset=utf8").times(2); @@ -754,7 +755,7 @@ private void verifyGetWSDLQuery() throws Exception { request.getPathInfo(); EasyMock.expectLastCall().andReturn("/bar/foo"); request.getCharacterEncoding(); - EasyMock.expectLastCall().andReturn("UTF-8"); + EasyMock.expectLastCall().andReturn(StandardCharsets.UTF_8.name()); request.getQueryString(); EasyMock.expectLastCall().andReturn("wsdl"); response.setContentType("text/xml"); diff --git a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/util/Utils.java b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/util/Utils.java index a8db2075262..d3800a34b1f 100644 --- a/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/util/Utils.java +++ b/rt/transports/http-netty/netty-server/src/main/java/org/apache/cxf/transport/http/netty/server/util/Utils.java @@ -24,6 +24,7 @@ import java.net.FileNameMap; import java.net.URLConnection; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -144,10 +145,10 @@ public static String getMimeType(String fileUrl) { public static String sanitizeUri(String uri) { // Decode the path. try { - uri = URLDecoder.decode(uri, "UTF-8"); + uri = URLDecoder.decode(uri, StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { try { - uri = URLDecoder.decode(uri, "ISO-8859-1"); + uri = URLDecoder.decode(uri, StandardCharsets.ISO_8859_1.name()); } catch (UnsupportedEncodingException e1) { throw new Error(); } diff --git a/rt/transports/http-netty/netty-server/src/test/java/org/apache/cxf/transport/http/netty/server/NettyHttpDestinationTest.java b/rt/transports/http-netty/netty-server/src/test/java/org/apache/cxf/transport/http/netty/server/NettyHttpDestinationTest.java index 2882b5d0baa..f08bd2a0fdd 100644 --- a/rt/transports/http-netty/netty-server/src/test/java/org/apache/cxf/transport/http/netty/server/NettyHttpDestinationTest.java +++ b/rt/transports/http-netty/netty-server/src/test/java/org/apache/cxf/transport/http/netty/server/NettyHttpDestinationTest.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.OutputStream; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -656,7 +657,7 @@ private void setUpDoService(boolean setRedirectURL, EasyMock.expect(request.getRequestURI()).andReturn("/foo"); EasyMock.expect(request.getRequestURL()) .andReturn(new StringBuffer("http://localhost/foo")).anyTimes(); - EasyMock.expect(request.getCharacterEncoding()).andReturn("UTF-8"); + EasyMock.expect(request.getCharacterEncoding()).andReturn(StandardCharsets.UTF_8.name()); EasyMock.expect(request.getQueryString()).andReturn(query).times(2); EasyMock.expect(request.getHeader("Accept")).andReturn("*/*"); EasyMock.expect(request.getContentType()).andReturn("text/xml charset=utf8").times(2); @@ -751,7 +752,7 @@ private void verifyGetWSDLQuery() throws Exception { request.getPathInfo(); EasyMock.expectLastCall().andReturn("/bar/foo"); request.getCharacterEncoding(); - EasyMock.expectLastCall().andReturn("UTF-8"); + EasyMock.expectLastCall().andReturn(StandardCharsets.UTF_8.name()); request.getQueryString(); EasyMock.expectLastCall().andReturn("wsdl"); response.setContentType("text/xml"); diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java index fe488119256..1c02bcc6ded 100644 --- a/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java +++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/https/httpclient/PublicSuffixMatcherLoader.java @@ -31,6 +31,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,7 +54,7 @@ private PublicSuffixMatcherLoader() { private static PublicSuffixMatcher load(final InputStream in) throws IOException { final PublicSuffixList list = new PublicSuffixListParser().parse( - new InputStreamReader(in, "UTF-8")); + new InputStreamReader(in, StandardCharsets.UTF_8)); return new PublicSuffixMatcher(list.getRules(), list.getExceptions()); } diff --git a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java index f5e6e9c2c4c..0c61622d2cd 100644 --- a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java +++ b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java @@ -24,6 +24,7 @@ import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.Collections; import java.util.Enumeration; @@ -277,7 +278,7 @@ static String getEncoding(String ct) throws UnsupportedEncodingException { } } - String normalizedEncoding = HttpHeaderHelper.mapCharset(enc, "UTF-8"); + String normalizedEncoding = HttpHeaderHelper.mapCharset(enc, StandardCharsets.UTF_8.name()); if (normalizedEncoding == null) { String m = new org.apache.cxf.common.i18n.Message("INVALID_ENCODING_MSG", LOG, new Object[] { enc diff --git a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/uri/JMSURIParser.java b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/uri/JMSURIParser.java index abcbec3df4f..1b48e4df7be 100644 --- a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/uri/JMSURIParser.java +++ b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/uri/JMSURIParser.java @@ -20,6 +20,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -94,7 +95,7 @@ public Map parseQuery() { private static String urldecode(String s) { try { - return URLDecoder.decode(s, "UTF-8"); + return URLDecoder.decode(s, StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException("Encoding UTF-8 not supported"); } diff --git a/rt/transports/jms/src/test/java/org/apache/cxf/transport/jms/JMSMessageUtilTest.java b/rt/transports/jms/src/test/java/org/apache/cxf/transport/jms/JMSMessageUtilTest.java index ec3e1280f66..ab54e2253b9 100644 --- a/rt/transports/jms/src/test/java/org/apache/cxf/transport/jms/JMSMessageUtilTest.java +++ b/rt/transports/jms/src/test/java/org/apache/cxf/transport/jms/JMSMessageUtilTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import org.junit.Assert; import org.junit.Test; @@ -29,8 +30,10 @@ public class JMSMessageUtilTest extends Assert { @Test public void testGetEncoding() throws IOException { - assertEquals("Get the wrong encoding", JMSMessageUtils.getEncoding("text/xml; charset=utf-8"), "UTF-8"); - assertEquals("Get the wrong encoding", JMSMessageUtils.getEncoding("text/xml"), "UTF-8"); + assertEquals("Get the wrong encoding", JMSMessageUtils.getEncoding("text/xml; charset=utf-8"), + StandardCharsets.UTF_8.name()); + assertEquals("Get the wrong encoding", JMSMessageUtils.getEncoding("text/xml"), + StandardCharsets.UTF_8.name()); assertEquals("Get the wrong encoding", JMSMessageUtils.getEncoding("text/xml; charset=GBK"), "GBK"); try { JMSMessageUtils.getEncoding("text/xml; charset=asci"); diff --git a/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/CapturingXMLWriter.java b/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/CapturingXMLWriter.java index 6d4925232a3..1fe31fbb2b8 100644 --- a/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/CapturingXMLWriter.java +++ b/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/CapturingXMLWriter.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.ws.rm; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -39,7 +40,7 @@ public class CapturingXMLWriter implements XMLStreamWriter { public CapturingXMLWriter(XMLStreamWriter del) { delegate = del; - capture = StaxUtils.createXMLStreamWriter(bos, "UTF-8"); + capture = StaxUtils.createXMLStreamWriter(bos, StandardCharsets.UTF_8.name()); Map map = new HashMap(); map.put("{http://schemas.xmlsoap.org/ws/2005/02/rm}Sequence", ""); diff --git a/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RetransmissionQueueImpl.java b/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RetransmissionQueueImpl.java index d4698c8a047..223430e603d 100644 --- a/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RetransmissionQueueImpl.java +++ b/rt/ws/rm/src/main/java/org/apache/cxf/ws/rm/soap/RetransmissionQueueImpl.java @@ -20,6 +20,7 @@ package org.apache.cxf.ws.rm.soap; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -761,7 +762,7 @@ private void doResend(SoapMessage message) { // read SOAP headers from saved input stream RewindableInputStream is = (RewindableInputStream)message.get(RMMessageConstants.SAVED_CONTENT); is.rewind(); - XMLStreamReader reader = StaxUtils.createXMLStreamReader(is, "UTF-8"); + XMLStreamReader reader = StaxUtils.createXMLStreamReader(is, StandardCharsets.UTF_8.name()); message.getHeaders().clear(); if (reader.getEventType() != XMLStreamConstants.START_ELEMENT && reader.nextTag() != XMLStreamConstants.START_ELEMENT) { diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JStaxOutInterceptor.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JStaxOutInterceptor.java index 3ba1f66dc28..102662402e1 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JStaxOutInterceptor.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JStaxOutInterceptor.java @@ -19,6 +19,7 @@ package org.apache.cxf.ws.security.wss4j; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.security.Provider; import java.util.LinkedList; import java.util.List; @@ -298,7 +299,7 @@ private String getEncoding(Message message) { } if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); message.put(Message.ENCODING, encoding); } return encoding; diff --git a/rt/wsdl/src/main/java/org/apache/cxf/wsdl/JAXBExtensionHelper.java b/rt/wsdl/src/main/java/org/apache/cxf/wsdl/JAXBExtensionHelper.java index f5bae666770..9b77c2689d6 100644 --- a/rt/wsdl/src/main/java/org/apache/cxf/wsdl/JAXBExtensionHelper.java +++ b/rt/wsdl/src/main/java/org/apache/cxf/wsdl/JAXBExtensionHelper.java @@ -21,6 +21,7 @@ import java.io.PrintWriter; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -288,7 +289,7 @@ public void marshall(@SuppressWarnings("rawtypes") Class parent, QName qname, // TODO Auto-generated method stub try { Marshaller u = createMarshaller(); - u.setProperty("jaxb.encoding", "UTF-8"); + u.setProperty("jaxb.encoding", StandardCharsets.UTF_8.name()); u.setProperty("jaxb.fragment", Boolean.TRUE); u.setProperty("jaxb.formatted.output", Boolean.TRUE); diff --git a/rt/wsdl/src/test/java/org/apache/cxf/wsdl11/ServiceWSDLBuilderTest.java b/rt/wsdl/src/test/java/org/apache/cxf/wsdl11/ServiceWSDLBuilderTest.java index 9e102fbb5ac..6e2b3e4910e 100644 --- a/rt/wsdl/src/test/java/org/apache/cxf/wsdl11/ServiceWSDLBuilderTest.java +++ b/rt/wsdl/src/test/java/org/apache/cxf/wsdl11/ServiceWSDLBuilderTest.java @@ -20,6 +20,7 @@ package org.apache.cxf.wsdl11; import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -359,7 +360,7 @@ public void testXsdImportMultipleSchemas() throws Exception { writer.close(); // this is a test to make sure any embedded namespaces are properly included - String savedSchema = new String(outputStream.toByteArray(), "UTF-8"); + String savedSchema = new String(outputStream.toByteArray(), StandardCharsets.UTF_8); assertTrue(savedSchema.contains("http://www.w3.org/2005/05/xmlmime")); SchemaImport types2SchemaImport = getImport(typesSchema.getImports(), diff --git a/services/ws-discovery/ws-discovery-api/src/test/java/org/apache/cxf/ws/discovery/WSDiscoveryClientTest.java b/services/ws-discovery/ws-discovery-api/src/test/java/org/apache/cxf/ws/discovery/WSDiscoveryClientTest.java index e7f7bbaca5a..f44217d5e78 100644 --- a/services/ws-discovery/ws-discovery-api/src/test/java/org/apache/cxf/ws/discovery/WSDiscoveryClientTest.java +++ b/services/ws-discovery/ws-discovery-api/src/test/java/org/apache/cxf/ws/discovery/WSDiscoveryClientTest.java @@ -26,6 +26,7 @@ import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -116,7 +117,7 @@ public void run() { DatagramPacket p = new DatagramPacket(bytes, bytes.length, address, Integer.parseInt(PORT)); s.receive(p); SocketAddress sa = p.getSocketAddress(); - String incoming = new String(p.getData(), 0, p.getLength(), "UTF-8"); + String incoming = new String(p.getData(), 0, p.getLength(), StandardCharsets.UTF_8); int idx = incoming.indexOf("MessageID"); idx = incoming.indexOf('>', idx); incoming = incoming.substring(idx + 1); @@ -127,7 +128,7 @@ public void run() { String msg = IOUtils.readStringFromStream(ins); msg = msg.replace("urn:uuid:883d0d53-92aa-4066-9b6f-9eadb1832366", incoming); - byte out[] = msg.getBytes("UTF-8"); + byte out[] = msg.getBytes(StandardCharsets.UTF_8); DatagramPacket outp = new DatagramPacket(out, 0, out.length, sa); s.send(outp); } diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java index 33ea4158b82..429cf0e0ece 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java @@ -26,6 +26,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -548,7 +549,7 @@ public void testGetBookWithColonMarks() throws Exception { // not disallowed in the path components String endpointAddressUrlEncoded = "http://localhost:" + PORT + "/bookstore/books/colon/" - + URLEncoder.encode("1:2:3", "UTF-8"); + + URLEncoder.encode("1:2:3", StandardCharsets.UTF_8.name()); Response r = WebClient.create(endpointAddressUrlEncoded).get(); assertEquals(404, r.getStatus()); diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerProxySpringBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerProxySpringBookTest.java index a2d1052ee4a..60200f0b44f 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerProxySpringBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerProxySpringBookTest.java @@ -24,6 +24,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -59,7 +60,7 @@ public static void startServers() throws Exception { public void testGetWadlResourcesInfo() throws Exception { WebClient client = WebClient.create("http://localhost:" + PORT + "/test" + "?_wadl&_type=xml"); WebClient.getConfig(client).getHttpConduit().getClient().setReceiveTimeout(10000000); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); Element root = doc.getDocumentElement(); assertEquals(WadlGenerator.WADL_NS, root.getNamespaceURI()); assertEquals("application", root.getLocalName()); diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringProviderTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringProviderTest.java index ff999516bba..f9dab3cd849 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringProviderTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringProviderTest.java @@ -28,6 +28,7 @@ import java.net.Socket; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -151,7 +152,7 @@ public void testPetStoreWadl() throws Exception { public void testWadlPublishedEndpointUrl() throws Exception { String requestURI = "http://localhost:" + PORT + "/webapp/resources2"; WebClient client = WebClient.create(requestURI + "?_wadl&_type=xml"); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); Element root = doc.getDocumentElement(); assertEquals(WadlGenerator.WADL_NS, root.getNamespaceURI()); assertEquals("application", root.getLocalName()); @@ -179,7 +180,7 @@ private void checkPetStoreInfo(Element resource) { private List getWadlResourcesInfo(String baseURI, String requestURI, int size) throws Exception { WebClient client = WebClient.create(requestURI + "?_wadl&_type=xml"); WebClient.getConfig(client).getHttpConduit().getClient().setReceiveTimeout(10000000); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); Element root = doc.getDocumentElement(); assertEquals(WadlGenerator.WADL_NS, root.getNamespaceURI()); assertEquals("application", root.getLocalName()); diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerSpringBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerSpringBookTest.java index 5ad1242111e..7a1d546e485 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerSpringBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerSpringBookTest.java @@ -26,6 +26,7 @@ import java.net.Socket; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -140,7 +141,7 @@ public void testPostGeneratedBook() throws Exception { public void testGetWadlFromWadlLocation() throws Exception { String address = "http://localhost:" + PORT + "/the/generated"; WebClient client = WebClient.create(address + "/bookstore" + "?_wadl&_type=xml"); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); List resources = checkWadlResourcesInfo(doc, address, "/schemas/book.xsd", 2); assertEquals("", resources.get(0).getAttribute("type")); String type = resources.get(1).getAttribute("type"); @@ -258,7 +259,7 @@ private void checkSchemas(String address, String schemaSegment, String refAttrName) throws Exception { WebClient client = WebClient.create(address + schemaSegment); WebClient.getConfig(client).getHttpConduit().getClient().setReceiveTimeout(10000000L); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); Element root = doc.getDocumentElement(); assertEquals(Constants.URI_2001_SCHEMA_XSD, root.getNamespaceURI()); assertEquals("schema", root.getLocalName()); @@ -277,7 +278,7 @@ private void checkWadlResourcesType(String baseURI, String requestTypeURI, Strin WebClient client = WebClient.create(requestTypeURI); WebClient.getConfig(client).getHttpConduit().getClient().setReceiveTimeout(1000000); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); Element root = doc.getDocumentElement(); assertEquals(WadlGenerator.WADL_NS, root.getNamespaceURI()); assertEquals("application", root.getLocalName()); @@ -300,7 +301,7 @@ private void checkWadlResourcesType(String baseURI, String requestTypeURI, Strin private List checkWadlResourcesInfo(String baseURI, String requestURI, String schemaRef, int size) throws Exception { WebClient client = WebClient.create(requestURI + "?_wadl&_type=xml"); - Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), "UTF-8")); + Document doc = StaxUtils.read(new InputStreamReader(client.get(InputStream.class), StandardCharsets.UTF_8)); return checkWadlResourcesInfo(doc, baseURI, schemaRef, size); } private List checkWadlResourcesInfo(Document doc, String baseURI, String schemaRef, int size) diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java index 97fd3898fb6..0ea788ac7e5 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -87,7 +88,7 @@ private static void configureLogging(String propFile) throws Exception { String s = IOUtils.readStringFromStream(ins); ins.close(); s = s.replaceAll("9080", PORT); - lm.readConfiguration(new ByteArrayInputStream(s.getBytes("UTF-8"))); + lm.readConfiguration(new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8))); for (Handler h : LOG.getHandlers()) { LOG.removeHandler(h); diff --git a/systests/jaxws/src/test/java/org/apache/cxf/systest/handlers/SoapFaultHandlerTest.java b/systests/jaxws/src/test/java/org/apache/cxf/systest/handlers/SoapFaultHandlerTest.java index d5c4992121f..e6829edc9a7 100644 --- a/systests/jaxws/src/test/java/org/apache/cxf/systest/handlers/SoapFaultHandlerTest.java +++ b/systests/jaxws/src/test/java/org/apache/cxf/systest/handlers/SoapFaultHandlerTest.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.StandardCharsets; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.test.AbstractCXFSpringTest; @@ -59,7 +60,7 @@ public void testFaultThrowingHandler() throws Exception { out.close(); InputStream response = getInputStream(connection); // get the response fault message - String result = IOUtils.toString(response, "UTF-8"); + String result = IOUtils.toString(response, StandardCharsets.UTF_8.name()); // just make sure the custom namespace is working assertTrue("The custom namespace is not working.", result.indexOf("cxf:Provider") > 0); diff --git a/systests/jaxws/src/test/java/org/apache/cxf/systest/swa/SwANoMimeServiceImpl.java b/systests/jaxws/src/test/java/org/apache/cxf/systest/swa/SwANoMimeServiceImpl.java index 00bff254457..8fa7b87117c 100644 --- a/systests/jaxws/src/test/java/org/apache/cxf/systest/swa/SwANoMimeServiceImpl.java +++ b/systests/jaxws/src/test/java/org/apache/cxf/systest/swa/SwANoMimeServiceImpl.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import javax.activation.DataHandler; import javax.jws.WebService; @@ -95,12 +96,7 @@ public OutputResponseAll echoAllAttachmentTypes(VoidRequest request, Holder text, Holder data) { - try { - data.value = ("test" + new String(data.value, 0, 6)).getBytes("UTF-8"); - } catch (IOException e) { - e.printStackTrace(); - } - + data.value = ("test" + new String(data.value, 0, 6)).getBytes(StandardCharsets.UTF_8); } public void echoDataRef(Holder data) { @@ -120,11 +116,7 @@ public void echoDataRef(Holder data) { } public void echoDataWithHeader(Holder text, Holder data, Holder headerText) { - try { - data.value = ("test" + new String(data.value, 0, 6)).getBytes("UTF-8"); - } catch (IOException e) { - e.printStackTrace(); - } + data.value = ("test" + new String(data.value, 0, 6)).getBytes(StandardCharsets.UTF_8); } } diff --git a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/jaxrs/kerberos/JAXRSKerberosBookTest.java b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/jaxrs/kerberos/JAXRSKerberosBookTest.java index 4087fcc1359..3f0c79f6c23 100644 --- a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/jaxrs/kerberos/JAXRSKerberosBookTest.java +++ b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/jaxrs/kerberos/JAXRSKerberosBookTest.java @@ -20,6 +20,7 @@ package org.apache.cxf.systest.kerberos.jaxrs.kerberos; import java.io.File; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -105,7 +106,7 @@ public void updatePort() throws Exception { // Read in krb5.conf and substitute in the correct port Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/krb5.conf"); - String content = new String(Files.readAllBytes(path), "UTF-8"); + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); content = content.replaceAll("port", "" + super.getKdcServer().getTransports()[0].getPort()); Path path2 = FileSystems.getDefault().getPath(basedir, "/target/test-classes/jaxrs.krb5.conf"); diff --git a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/ldap/LDAPClaimsTest.java b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/ldap/LDAPClaimsTest.java index bdd2f80d758..6d2045ec1b7 100644 --- a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/ldap/LDAPClaimsTest.java +++ b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/ldap/LDAPClaimsTest.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.InputStream; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -113,7 +114,7 @@ public void updatePort() throws Exception { // Read in ldap.xml and substitute in the correct port Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/ldap.xml"); - String content = new String(Files.readAllBytes(path), "UTF-8"); + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); content = content.replaceAll("portno", "" + super.getLdapServer().getPort()); Path path2 = FileSystems.getDefault().getPath(basedir, "/target/test-classes/ldapport.xml"); diff --git a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/kerberos/KerberosTokenTest.java b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/kerberos/KerberosTokenTest.java index 648dc73bc5b..be57236c4b3 100644 --- a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/kerberos/KerberosTokenTest.java +++ b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/kerberos/KerberosTokenTest.java @@ -21,6 +21,7 @@ import java.io.File; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -118,7 +119,7 @@ public void updatePort() throws Exception { // Read in krb5.conf and substitute in the correct port Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/krb5.conf"); - String content = new String(Files.readAllBytes(path), "UTF-8"); + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); content = content.replaceAll("port", "" + super.getKdcServer().getTransports()[0].getPort()); Path path2 = FileSystems.getDefault().getPath(basedir, "/target/test-classes/wssec.kerberos.krb5.conf"); diff --git a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/spnego/SpnegoTokenTest.java b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/spnego/SpnegoTokenTest.java index a7948bc6e9d..619e16ace4a 100644 --- a/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/spnego/SpnegoTokenTest.java +++ b/systests/kerberos/src/test/java/org/apache/cxf/systest/kerberos/wssec/spnego/SpnegoTokenTest.java @@ -21,6 +21,7 @@ import java.io.File; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -111,7 +112,7 @@ public void updatePort() throws Exception { // Read in krb5.conf and substitute in the correct port Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/krb5.conf"); - String content = new String(Files.readAllBytes(path), "UTF-8"); + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); content = content.replaceAll("port", "" + super.getKdcServer().getTransports()[0].getPort()); Path path2 = FileSystems.getDefault().getPath(basedir, "/target/test-classes/wssec.spnego.krb5.conf"); diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth/MemoryOAuthDataProvider.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth/MemoryOAuthDataProvider.java index a330adf7c10..799c37911ce 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth/MemoryOAuthDataProvider.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth/MemoryOAuthDataProvider.java @@ -19,6 +19,7 @@ package org.apache.cxf.systest.jaxrs.security.oauth; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -156,7 +157,7 @@ public void removeToken(Token t) { protected String generateToken() throws OAuthServiceException { String token; try { - token = tokenGenerator.generate(UUID.randomUUID().toString().getBytes("UTF-8")); + token = tokenGenerator.generate(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { throw new OAuthServiceException("Unable to create token ", e.getCause()); } diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFFilterTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFFilterTest.java index f6881d0af5e..4ee4b452830 100644 --- a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFFilterTest.java +++ b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFFilterTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.systest.servlet; +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; @@ -72,7 +73,7 @@ public void testPostInvokeServices() throws Exception { WebResponse response = newClient().getResponse(req); assertEquals("text/xml", response.getContentType()); - assertEquals("UTF-8", response.getCharacterSet()); + assertEquals(StandardCharsets.UTF_8.name(), response.getCharacterSet()); Document doc = StaxUtils.read(response.getInputStream()); assertNotNull(doc); diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFServletTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFServletTest.java index 49e3e797909..a60f7917b9b 100644 --- a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFServletTest.java +++ b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/CXFServletTest.java @@ -20,6 +20,7 @@ +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; @@ -67,7 +68,7 @@ protected Bus createBus() throws BusException { @Test public void testPostInvokeServices() throws Exception { - invoke("UTF-8"); + invoke(StandardCharsets.UTF_8.name()); invoke("iso-8859-1"); } diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/ExternalServicesServletTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/ExternalServicesServletTest.java index 05f0ae86642..a96b449163d 100644 --- a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/ExternalServicesServletTest.java +++ b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/ExternalServicesServletTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.systest.servlet; +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; @@ -91,7 +92,7 @@ public void testPostInvokeServices() throws Exception { WebResponse response = newClient().getResponse(req); assertEquals("text/xml", response.getContentType()); - assertEquals("UTF-8", response.getCharacterSet()); + assertEquals(StandardCharsets.UTF_8.name(), response.getCharacterSet()); Document doc = StaxUtils.read(response.getInputStream()); assertNotNull(doc); diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/JsFrontEndServletTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/JsFrontEndServletTest.java index 5e94811c9c8..4f0c1e88c35 100644 --- a/systests/transports/src/test/java/org/apache/cxf/systest/servlet/JsFrontEndServletTest.java +++ b/systests/transports/src/test/java/org/apache/cxf/systest/servlet/JsFrontEndServletTest.java @@ -44,7 +44,7 @@ public void testPostInvokeServices() throws Exception { WebResponse response = newClient().getResponse(req); assertEquals("text/xml", response.getContentType()); - //assertEquals("UTF-8", response.getCharacterSet()); + //assertEquals(StandardCharsets.UTF_8, response.getCharacterSet()); Document doc = StaxUtils.read(response.getInputStream()); assertNotNull(doc); diff --git a/systests/uncategorized/src/test/java/org/apache/cxf/systest/js/Server.java b/systests/uncategorized/src/test/java/org/apache/cxf/systest/js/Server.java index 06ff4ac8994..7a574ecf5a3 100644 --- a/systests/uncategorized/src/test/java/org/apache/cxf/systest/js/Server.java +++ b/systests/uncategorized/src/test/java/org/apache/cxf/systest/js/Server.java @@ -21,6 +21,7 @@ import java.io.File; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import org.apache.cxf.js.rhino.ProviderFactory; import org.apache.cxf.testutil.common.AbstractBusTestServerBase; @@ -33,11 +34,11 @@ public class Server extends AbstractBusTestServerBase { protected void run() { try { String f = getClass().getResource("resources/hello_world.js").toURI().getPath(); - f = URLDecoder.decode(f, "UTF-8"); + f = URLDecoder.decode(f, StandardCharsets.UTF_8.name()); pf.createAndPublish(new File(f), "http://localhost:" + JS_PORT + "/SoapContext/SoapPort", false); f = getClass().getResource("resources/hello_world.jsx").toURI().getPath(); - f = URLDecoder.decode(f, "UTF-8"); + f = URLDecoder.decode(f, StandardCharsets.UTF_8.name()); pf.createAndPublish(new File(f), "http://localhost:" + JSX_PORT, false); } catch (Exception ex) { ex.printStackTrace(); diff --git a/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/MtomServerTest.java b/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/MtomServerTest.java index 78a1864336c..4285378f4c1 100644 --- a/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/MtomServerTest.java +++ b/systests/uncategorized/src/test/java/org/apache/cxf/systest/mtom/MtomServerTest.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -169,10 +170,10 @@ public void testURLBasedAttachment() throws Exception { } ByteArrayOutputStream bout = new ByteArrayOutputStream(); IOUtils.copy(is, bout); - String s = bout.toString("UTF-8"); + String s = bout.toString(StandardCharsets.UTF_8.name()); s = s.replaceAll(":9036/", ":" + PORT2 + "/"); - os.write(s.getBytes("UTF-8")); + os.write(s.getBytes(StandardCharsets.UTF_8)); os.flush(); is.close(); os.close(); diff --git a/tools/common/src/main/java/org/apache/cxf/tools/util/FileWriterUtil.java b/tools/common/src/main/java/org/apache/cxf/tools/util/FileWriterUtil.java index 79d18ada640..85039f8925f 100644 --- a/tools/common/src/main/java/org/apache/cxf/tools/util/FileWriterUtil.java +++ b/tools/common/src/main/java/org/apache/cxf/tools/util/FileWriterUtil.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.logging.Logger; import org.apache.cxf.common.i18n.Message; @@ -58,12 +59,12 @@ public File getFileToWrite(String packageName, String fileName) throws IOExcepti } private Writer getWriter(File fn) throws IOException { - return getWriter(fn, "UTF-8"); + return getWriter(fn, StandardCharsets.UTF_8.name()); } public Writer getWriter(File fn, String encoding) throws IOException { if (encoding == null) { - encoding = "UTF-8"; + encoding = StandardCharsets.UTF_8.name(); } return new OutputStreamWriter(new BufferedOutputStream(osc.createOutputStream(fn)), encoding); } diff --git a/tools/common/src/test/java/org/apache/cxf/tools/util/URIParserUtilTest.java b/tools/common/src/test/java/org/apache/cxf/tools/util/URIParserUtilTest.java index 9bb36429528..1760a89948b 100644 --- a/tools/common/src/test/java/org/apache/cxf/tools/util/URIParserUtilTest.java +++ b/tools/common/src/test/java/org/apache/cxf/tools/util/URIParserUtilTest.java @@ -20,6 +20,7 @@ package org.apache.cxf.tools.util; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import org.apache.cxf.common.util.URIParserUtil; import org.junit.Assert; @@ -98,9 +99,9 @@ public void testGetAbsoluteURI() throws Exception { } @Test public void testCXF3855() throws Exception { - String orig = new String(new byte[] {-47, -122}, "UTF-8"); + String orig = new String(new byte[] {-47, -122}, StandardCharsets.UTF_8); orig = "/foo" + orig + ".txt"; String s = URIParserUtil.escapeChars(orig); - assertEquals(orig, URLDecoder.decode(s, "UTF-8")); + assertEquals(orig, URLDecoder.decode(s, StandardCharsets.UTF_8.name())); } } diff --git a/tools/corba/src/main/java/org/apache/cxf/tools/corba/processors/idl/IDLToWSDLProcessor.java b/tools/corba/src/main/java/org/apache/cxf/tools/corba/processors/idl/IDLToWSDLProcessor.java index 186f3b5625c..fe339706c8d 100644 --- a/tools/corba/src/main/java/org/apache/cxf/tools/corba/processors/idl/IDLToWSDLProcessor.java +++ b/tools/corba/src/main/java/org/apache/cxf/tools/corba/processors/idl/IDLToWSDLProcessor.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.Writer; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -414,7 +415,7 @@ public Writer getOutputWriter(File file) throws Exception { String encoding = env.get(ToolCorbaConstants.CFG_WSDL_ENCODING).toString(); return new FileWriterUtil().getWriter(file, encoding); } else { - return new FileWriterUtil().getWriter(file, "UTF-8"); + return new FileWriterUtil().getWriter(file, StandardCharsets.UTF_8.name()); } } diff --git a/tools/corba/src/test/java/org/apache/cxf/tools/corba/WSDLToIDLTest.java b/tools/corba/src/test/java/org/apache/cxf/tools/corba/WSDLToIDLTest.java index b338d75228c..999cd6fe8bf 100644 --- a/tools/corba/src/test/java/org/apache/cxf/tools/corba/WSDLToIDLTest.java +++ b/tools/corba/src/test/java/org/apache/cxf/tools/corba/WSDLToIDLTest.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -174,7 +175,7 @@ public void testIDLGenDefault() throws Exception { Path path = FileSystems.getDefault().getPath(output.getPath(), "simple-binding.idl"); assertTrue("simple-binding.idl should be generated", Files.isReadable(path)); - String line = new String(Files.readAllBytes(path), "UTF-8"); + String line = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); assertTrue("Invalid Idl File Generated", line.length() > 0); } @@ -189,7 +190,7 @@ public void testIDLGenSpecifiedFile() throws Exception { Path path = FileSystems.getDefault().getPath(output.getPath(), "simple-binding_gen.idl"); assertTrue("simple-binding_gen.idl should be generated", Files.isReadable(path)); - String line = new String(Files.readAllBytes(path), "UTF-8"); + String line = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); assertTrue("Invalid Idl File Generated", line.length() > 0); } @@ -219,7 +220,7 @@ public void testBindAndIDLGen() throws Exception { fail("WSDLToIDL generated an invalid simple-binding-corba.wsdl"); } - String line = new String(Files.readAllBytes(path2), "UTF-8"); + String line = new String(Files.readAllBytes(path2), StandardCharsets.UTF_8); assertTrue("Invalid Idl File Generated", line.length() > 0); } diff --git a/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/DateTypeCustomGenerator.java b/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/DateTypeCustomGenerator.java index d2e583f6353..70ef1d33e87 100644 --- a/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/DateTypeCustomGenerator.java +++ b/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/DateTypeCustomGenerator.java @@ -20,6 +20,7 @@ import java.io.File; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -95,7 +96,7 @@ public File generate(File outputdir) { try { generator.doWrite(getTemplate(), new FileWriterUtil(xjb.getParent(), getOutputStreamCreator()) - .getWriter(xjb, "UTF-8")); + .getWriter(xjb, StandardCharsets.UTF_8.name())); } catch (Exception e) { e.printStackTrace(); } diff --git a/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/WSDL11Generator.java b/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/WSDL11Generator.java index 257889d14fc..46954ea8bbf 100644 --- a/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/WSDL11Generator.java +++ b/tools/javato/ws/src/main/java/org/apache/cxf/tools/java2wsdl/generator/wsdl11/WSDL11Generator.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -70,7 +71,7 @@ public Definition generate(final File dir) { Definition def = null; try { Writer os = new FileWriterUtil(file.getParent(), - getOutputStreamCreator()).getWriter(file, "UTF-8"); + getOutputStreamCreator()).getWriter(file, StandardCharsets.UTF_8.name()); WSDLWriter wsdlWriter = WSDLFactory.newInstance().newWSDLWriter(); ServiceWSDLBuilder builder = new ServiceWSDLBuilder(getBus(), getServiceModel()); @@ -105,8 +106,9 @@ public Definition generate(final File dir) { File impfile = new File(file.getParentFile(), imp.getKey()); Element el = imp.getValue().getElement(); updateImports(el, imports); - os = new FileWriterUtil(impfile.getParent(), - getToolContext().get(OutputStreamCreator.class)).getWriter(impfile, "UTF-8"); + FileWriterUtil fileWriterUtil = + new FileWriterUtil(impfile.getParent(), getToolContext().get(OutputStreamCreator.class)); + os = fileWriterUtil.getWriter(impfile, StandardCharsets.UTF_8.name()); StaxUtils.writeTo(el, os, 2); os.close(); } diff --git a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainer.java b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainer.java index 687e533e3d6..ec36bc02788 100644 --- a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainer.java +++ b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainer.java @@ -24,6 +24,7 @@ import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -205,7 +206,7 @@ private void processWadl() { protected String readWadl(String wadlURI) { try { URL url = new URL(wadlURI); - Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); + Reader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8); return IOUtils.toString(reader); } catch (IOException e) { throw new ToolException(e); diff --git a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java index 285f85fe5bd..ff37819e443 100644 --- a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java +++ b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxrs/SourceGenerator.java @@ -31,6 +31,7 @@ import java.io.Writer; import java.net.URI; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1666,7 +1667,7 @@ private void createJavaSourceFile(File src, QName qname, StringBuilder sbCode, S try { file.createNewFile(); try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), - encoding == null ? "UTF-8" : encoding)) { + encoding == null ? StandardCharsets.UTF_8.name() : encoding)) { writer.write(content); writer.flush(); } @@ -1683,7 +1684,7 @@ private Application readWadl(String wadl, String docPath) { if (validateWadl) { SchemaFactory factory = SchemaFactory.newInstance(Constants.URI_2001_SCHEMA_XSD); URL schemaURL = ResourceUtils.getResourceURL("classpath:/schemas/wadl/wadl.xsd", bus); - Reader r = new BufferedReader(new InputStreamReader(schemaURL.openStream(), "UTF-8")); + Reader r = new BufferedReader(new InputStreamReader(schemaURL.openStream(), StandardCharsets.UTF_8)); StreamSource source = new StreamSource(r); source.setSystemId(schemaURL.toString()); Schema s = factory.newSchema(new Source[]{source}); @@ -1714,7 +1715,8 @@ private Element readXmlDocument(Reader reader) { private void generateClassesFromSchema(JCodeModel codeModel, File src) { try { - Object writer = JAXBUtils.createFileCodeWriter(src, encoding == null ? "UTF-8" : encoding); + Object writer = JAXBUtils.createFileCodeWriter(src, encoding == null + ? StandardCharsets.UTF_8.name() : encoding); codeModel.build(writer); generatedTypeClasses = JAXBUtils.getGeneratedClassNames(codeModel); } catch (Exception e) { @@ -1814,7 +1816,7 @@ private Element readIncludedDocument(String href) { if (is == null) { is = URI.create(href).toURL().openStream(); } - return readXmlDocument(new InputStreamReader(is, "UTF-8")); + return readXmlDocument(new InputStreamReader(is, StandardCharsets.UTF_8)); } catch (Exception ex) { throw new RuntimeException("Resource " + href + " can not be read"); } diff --git a/tools/wsdlto/core/src/main/java/org/apache/cxf/tools/wsdlto/WSDLToJavaContainer.java b/tools/wsdlto/core/src/main/java/org/apache/cxf/tools/wsdlto/WSDLToJavaContainer.java index dac902fd60d..01f073bdccf 100644 --- a/tools/wsdlto/core/src/main/java/org/apache/cxf/tools/wsdlto/WSDLToJavaContainer.java +++ b/tools/wsdlto/core/src/main/java/org/apache/cxf/tools/wsdlto/WSDLToJavaContainer.java @@ -32,6 +32,7 @@ import java.io.Writer; import java.net.URI; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -820,7 +821,7 @@ private void generateLocalWSDL(ToolContext context) { Element el = imp.getSchemaDocument().getDocumentElement(); updateImports(el, sourceMap); os = new FileWriterUtil(impfile.getParent(), context.get(OutputStreamCreator.class)) - .getWriter(impfile, "UTF-8"); + .getWriter(impfile, StandardCharsets.UTF_8.name()); StaxUtils.writeTo(el, os, 2); os.close(); } diff --git a/tools/wsdlto/frontend/javascript/src/test/java/org/apache/cxf/tools/wsdlto/javascript/WSDLToJavaScriptTest.java b/tools/wsdlto/frontend/javascript/src/test/java/org/apache/cxf/tools/wsdlto/javascript/WSDLToJavaScriptTest.java index 10cb2cf020a..5a365040f6e 100644 --- a/tools/wsdlto/frontend/javascript/src/test/java/org/apache/cxf/tools/wsdlto/javascript/WSDLToJavaScriptTest.java +++ b/tools/wsdlto/frontend/javascript/src/test/java/org/apache/cxf/tools/wsdlto/javascript/WSDLToJavaScriptTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.tools.wsdlto.javascript; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -60,7 +61,7 @@ public void testGeneration() throws Exception { // now we really want to check some results. Path path = FileSystems.getDefault().getPath(output.getPath(), "SOAPService_Test1.js"); assertTrue(Files.isReadable(path)); - String javascript = new String(Files.readAllBytes(path), "UTF-8"); + String javascript = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); assertTrue(javascript.contains("xmlns:murble='http://apache.org/hello_world_soap_http'")); assertEquals("Number of '{' does not match number of '}' in generated JavaScript.", @@ -84,7 +85,7 @@ public void testCXF3891() throws Exception { // now we really want to check some results. Path path = FileSystems.getDefault().getPath(output.getPath(), "SOAPService.js"); assertTrue(Files.isReadable(path)); - String javascript = new String(Files.readAllBytes(path), "UTF-8"); + String javascript = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); assertTrue(javascript.contains("xmlns:murble='http://apache.org/hello_world_soap_http'")); assertEquals("Number of '{' does not match number of '}' in generated JavaScript.", diff --git a/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java b/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java index c6eea4762a5..ef0fa797779 100644 --- a/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java +++ b/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java @@ -23,6 +23,7 @@ import java.io.FileInputStream; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -227,7 +228,7 @@ public void testBug305772() throws Exception { Path path = FileSystems.getDefault().getPath(output.getCanonicalPath(), "build.xml"); assertTrue(Files.isReadable(path)); - String content = new String(Files.readAllBytes(path), "UTF-8"); + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); assertTrue("wsdl location should be url style in build.xml", content.indexOf("param1=\"file:") > -1); From 7923a6209a05d71cfeb752ed2d45c9e6e7116942 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 11 Nov 2015 10:01:34 +0000 Subject: [PATCH 0036/1346] Fixing dist build --- .../demo/oauth/server/controllers/ApplicationController.java | 1 + .../demo/oauth/server/controllers/MemoryOAuthDataProvider.java | 1 + 2 files changed, 2 insertions(+) diff --git a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java index a6c72d30473..e49eb37eb80 100644 --- a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java +++ b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/ApplicationController.java @@ -18,6 +18,7 @@ */ package demo.oauth.server.controllers; +import java.nio.charset.StandardCharsets; import java.security.Principal; import java.security.SecureRandom; import java.util.Set; diff --git a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java index 37424a03ac6..31b322abef7 100644 --- a/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java +++ b/distribution/src/main/release/samples/oauth/server/src/main/java/demo/oauth/server/controllers/MemoryOAuthDataProvider.java @@ -19,6 +19,7 @@ package demo.oauth.server.controllers; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; From e7d7190b89ecf2ab9bd100ede621ebd6a2808dcd Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 11 Nov 2015 10:58:29 +0000 Subject: [PATCH 0037/1346] Fix SAML Audience Restriction problem with JMS --- .../java/org/apache/cxf/transport/jms/JMSMessageUtils.java | 4 +++- .../org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java | 2 +- .../apache/cxf/systest/jms/security/JMSWSSecurityTest.java | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java index 0c61622d2cd..3975b63107f 100644 --- a/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java +++ b/rt/transports/jms/src/main/java/org/apache/cxf/transport/jms/JMSMessageUtils.java @@ -154,7 +154,6 @@ private static void populateIncomingContext(javax.jms.Message message, inMessage.put(org.apache.cxf.message.Message.PROTOCOL_HEADERS, protHeaders); populateIncomingMessageProperties(message, inMessage, messageProperties); - } /** @@ -209,6 +208,9 @@ private static void populateIncomingMessageProperties(Message jmsMessage, headers.put(JMSSpecConstants.TARGET_SERVICE_IN_REQUESTURI, Collections.singletonList("true")); } + if (requestURI != null) { + inMessage.put(org.apache.cxf.message.Message.REQUEST_URI, requestURI); + } } catch (Exception e) { headers.put(JMSSpecConstants.MALFORMED_REQUESTURI, Collections.singletonList("true")); } diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java index d78a0697003..20b70a5a391 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JInInterceptor.java @@ -345,7 +345,7 @@ private void configureAudienceRestriction(SoapMessage msg, RequestData reqData) if (msg.get(org.apache.cxf.message.Message.REQUEST_URL) != null) { audiences.add((String)msg.get(org.apache.cxf.message.Message.REQUEST_URL)); } else if (msg.get(org.apache.cxf.message.Message.REQUEST_URI) != null) { - audiences.add((String)msg.get(org.apache.cxf.message.Message.REQUEST_URL)); + audiences.add((String)msg.get(org.apache.cxf.message.Message.REQUEST_URI)); } if (msg.getContextualProperty("javax.xml.ws.wsdl.service") != null) { diff --git a/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java b/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java index 6db3729dc95..fc8ffb2c46b 100644 --- a/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java +++ b/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java @@ -112,7 +112,6 @@ public void testUnsignedSAML2Token() throws Exception { } @Test - @org.junit.Ignore public void testUnsignedSAML2AudienceRestrictionToken() throws Exception { QName serviceName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldService"); QName portName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldPort"); @@ -129,7 +128,7 @@ public void testUnsignedSAML2AudienceRestrictionToken() throws Exception { ConditionsBean conditions = new ConditionsBean(); conditions.setTokenPeriodMinutes(5); List audiences = new ArrayList<>(); - audiences.add("http://apache.org/one"); + audiences.add("jms:jndi:dynamicQueues/test.jmstransport.text"); AudienceRestrictionBean audienceRestrictionBean = new AudienceRestrictionBean(); audienceRestrictionBean.setAudienceURIs(audiences); conditions.setAudienceRestrictions(Collections.singletonList(audienceRestrictionBean)); From 43d825116e02963bdc2625b3936fc64891c40504 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 11 Nov 2015 12:37:30 +0000 Subject: [PATCH 0038/1346] Adding some JWT authentication/authorization tests --- .../jose/jwt/BookServerJwtAuthnAuthz.java | 59 ++++ .../security/jose/jwt/BookStoreAuthn.java | 75 +++++ .../security/jose/jwt/JWTAuthnAuthzTest.java | 256 ++++++++++++++++++ .../security/jose/jwt/authn-authz-server.xml | 89 ++++++ 4 files changed, 479 insertions(+) create mode 100644 systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAuthnAuthz.java create mode 100644 systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookStoreAuthn.java create mode 100644 systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAuthnAuthzTest.java create mode 100644 systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/authn-authz-server.xml diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAuthnAuthz.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAuthnAuthz.java new file mode 100644 index 00000000000..3fb107d938b --- /dev/null +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookServerJwtAuthnAuthz.java @@ -0,0 +1,59 @@ +/** + * 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.systest.jaxrs.security.jose.jwt; + +import java.net.URL; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusTestServerBase; +import org.apache.cxf.testutil.common.TestUtil; + +public class BookServerJwtAuthnAuthz extends AbstractBusTestServerBase { + public static final String PORT = TestUtil.getPortNumber("jaxrs-jwt-authn-authz"); + private static final URL SERVER_CONFIG_FILE = + BookServerJwtAuthnAuthz.class.getResource("authn-authz-server.xml"); + + protected void run() { + SpringBusFactory bf = new SpringBusFactory(); + Bus springBus = bf.createBus(SERVER_CONFIG_FILE); + BusFactory.setDefaultBus(springBus); + setBus(springBus); + + try { + new BookServerJwtAuthnAuthz(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) { + try { + BookServerJwtAuthnAuthz s = new BookServerJwtAuthnAuthz(); + s.start(); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(-1); + } finally { + System.out.println("done!"); + } + } +} diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookStoreAuthn.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookStoreAuthn.java new file mode 100644 index 00000000000..5d5116d6c90 --- /dev/null +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/BookStoreAuthn.java @@ -0,0 +1,75 @@ +/** + * 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.systest.jaxrs.security.jose.jwt; + + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; + +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.systest.jaxrs.security.Book; +import org.junit.Assert; + +@Path("/bookstore") +public class BookStoreAuthn { + + @Context + MessageContext jaxrsContext; + + public BookStoreAuthn() { + } + + @POST + @Path("/books") + @Produces("text/plain") + @Consumes("text/plain") + public String echoText(String text) { + checkAuthentication(); + return text; + } + + @POST + @Path("/books") + @Produces("application/json") + @Consumes("application/json") + public Book echoBook(Book book) { + checkAuthentication(); + return book; + } + + @POST + @Path("/books") + @Produces("application/xml") + @Consumes("application/xml") + public Book echoBook2(Book book) { + checkAuthentication(); + return book; + } + + private void checkAuthentication() { + // Check that we have an authenticated principal + Assert.assertNotNull(jaxrsContext.getSecurityContext().getUserPrincipal()); + } +} + + diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAuthnAuthzTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAuthnAuthzTest.java new file mode 100644 index 00000000000..7f62b831026 --- /dev/null +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwt/JWTAuthnAuthzTest.java @@ -0,0 +1,256 @@ +/** + * 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.systest.jaxrs.security.jose.jwt; + +import java.net.URL; +import java.security.Security; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; + +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.rs.security.jose.jaxrs.JwtAuthenticationClientFilter; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.systest.jaxrs.security.Book; +import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * Some tests for authentication and authorization using JWT tokens. + */ +public class JWTAuthnAuthzTest extends AbstractBusClientServerTestBase { + public static final String PORT = BookServerJwtAuthnAuthz.PORT; + + @BeforeClass + public static void startServers() throws Exception { + assertTrue("server did not launch correctly", + launchServer(BookServerJwtAuthnAuthz.class, true)); + registerBouncyCastleIfNeeded(); + } + + private static void registerBouncyCastleIfNeeded() throws Exception { + // Still need it for Oracle Java 7 and Java 8 + Security.addProvider(new BouncyCastleProvider()); + } + + @AfterClass + public static void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + @org.junit.Test + public void testAuthentication() throws Exception { + + URL busFile = JWTAuthnAuthzTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testAuthenticationFailure() throws Exception { + + URL busFile = JWTAuthnAuthzTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwt/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jks"); + properties.put("rs.security.keystore.password", "password"); + properties.put("rs.security.key.password", "password"); + properties.put("rs.security.keystore.alias", "alice"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/alice.jks"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testAuthorization() throws Exception { + + URL busFile = JWTAuthnAuthzTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwtauthz/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + // The endpoint requires a role of "boss" + claims.setProperty("role", "boss"); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertEquals(response.getStatus(), 200); + + Book returnedBook = response.readEntity(Book.class); + assertEquals(returnedBook.getName(), "book"); + assertEquals(returnedBook.getId(), 123L); + } + + @org.junit.Test + public void testAuthorizationNoRole() throws Exception { + + URL busFile = JWTAuthnAuthzTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwtauthz/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + + @org.junit.Test + public void testAuthorizationWrongRole() throws Exception { + + URL busFile = JWTAuthnAuthzTest.class.getResource("client.xml"); + + List providers = new ArrayList(); + providers.add(new JacksonJsonProvider()); + providers.add(new JwtAuthenticationClientFilter()); + + String address = "https://localhost:" + PORT + "/signedjwtauthz/bookstore/books"; + WebClient client = + WebClient.create(address, providers, busFile.toString()); + client.type("application/json").accept("application/json"); + + // Create the JWT Token + JwtClaims claims = new JwtClaims(); + claims.setSubject("alice"); + claims.setIssuer("DoubleItSTSIssuer"); + claims.setIssuedAt(new Date().getTime() / 1000L); + claims.setProperty("role", "manager"); + + JwtToken token = new JwtToken(claims); + + Map properties = new HashMap(); + properties.put("rs.security.keystore.type", "jwk"); + properties.put("rs.security.keystore.alias", "2011-04-29"); + properties.put("rs.security.keystore.file", + "org/apache/cxf/systest/jaxrs/security/certs/jwkPrivateSet.txt"); + properties.put("rs.security.signature.algorithm", "RS256"); + properties.put(JwtConstants.JWT_TOKEN, token); + WebClient.getConfig(client).getRequestContext().putAll(properties); + + Response response = client.post(new Book("book", 123L)); + assertNotEquals(response.getStatus(), 200); + } + +} diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/authn-authz-server.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/authn-authz-server.xml new file mode 100644 index 00000000000..9f7f477b463 --- /dev/null +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwt/authn-authz-server.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From bdff406350ef16dfa61fd02288d6c5e68217da55 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 11 Nov 2015 12:55:19 +0000 Subject: [PATCH 0039/1346] Making it easier to load JWS/JWE properties from the custom code --- .../cxf/rs/security/jose/jwe/JweUtils.java | 64 ++++++++----- .../security/jose/jws/JwsCompactProducer.java | 11 +-- .../cxf/rs/security/jose/jws/JwsUtils.java | 92 +++++++++++++------ 3 files changed, 108 insertions(+), 59 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index a40c619be0b..074dfa5e0be 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -287,13 +287,11 @@ public static JweEncryptionProvider loadEncryptionProvider(boolean required) { } @SuppressWarnings("deprecation") public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, boolean required) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, required, - JoseConstants.RSSEC_ENCRYPTION_OUT_PROPS, - JoseConstants.RSSEC_ENCRYPTION_PROPS); + Properties props = loadEncryptionOutProperties(required); if (props == null) { return null; } + Message m = PhaseInterceptorChain.getCurrentMessage(); boolean includeCert = headers != null && MessageUtils.getContextualBoolean( @@ -302,7 +300,7 @@ public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, b m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1, false); KeyEncryptionProvider keyEncryptionProvider = null; - String keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, null, null); + String keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, null, null); KeyAlgorithm keyAlgo = KeyAlgorithm.getAlgorithm(keyEncryptionAlgo); String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentEncryptionAlgo); @@ -313,8 +311,8 @@ public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, b contentEncryptionAlgo = getContentEncryptionAlgo(m, props, jwk.getAlgorithm()); ctEncryptionProvider = getContentEncryptionAlgorithm(jwk, contentEncryptionAlgo); } else { - keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, jwk.getAlgorithm(), - getDefaultKeyAlgo(jwk)); + keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, jwk.getAlgorithm(), + getDefaultKeyAlgorithm(jwk)); keyEncryptionProvider = getKeyEncryptionProvider(jwk, keyAlgo); boolean includePublicKey = headers != null && MessageUtils.getContextualBoolean( @@ -366,18 +364,15 @@ public static JweDecryptionProvider loadDecryptionProvider(boolean required) { return loadDecryptionProvider(null, required); } public static JweDecryptionProvider loadDecryptionProvider(JweHeaders inHeaders, boolean required) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, required, - JoseConstants.RSSEC_ENCRYPTION_IN_PROPS, - JoseConstants.RSSEC_ENCRYPTION_PROPS); + Properties props = loadEncryptionInProperties(required); if (props == null) { return null; } - + Message m = PhaseInterceptorChain.getCurrentMessage(); KeyDecryptionProvider keyDecryptionProvider = null; String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); SecretKey ctDecryptionKey = null; - String keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, null, null); + String keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, null, null); if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { // Supporting loading a private key via a certificate for now List chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); @@ -412,8 +407,8 @@ public static JweDecryptionProvider loadDecryptionProvider(JweHeaders inHeaders, contentEncryptionAlgo = getContentEncryptionAlgo(m, props, jwk.getAlgorithm()); ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentEncryptionAlgo); } else { - keyEncryptionAlgo = getKeyEncryptionAlgo(m, props, jwk.getAlgorithm(), - getDefaultKeyAlgo(jwk)); + keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, jwk.getAlgorithm(), + getDefaultKeyAlgorithm(jwk)); keyDecryptionProvider = getKeyDecryptionProvider(jwk, KeyAlgorithm.getAlgorithm(keyEncryptionAlgo)); } @@ -641,7 +636,7 @@ private static JweDecryptionProvider createJweDecryptionProvider(KeyDecryptionPr } } @SuppressWarnings("deprecation") - private static String getKeyEncryptionAlgo(Message m, Properties props, + public static String getKeyEncryptionAlgorithm(Message m, Properties props, String algo, String defaultAlgo) { if (algo == null) { if (defaultAlgo == null) { @@ -649,7 +644,10 @@ private static String getKeyEncryptionAlgo(Message m, Properties props, } // Check for deprecated identifier first - String encAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM); + String encAlgo = null; + if (props != null) { + encAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM); + } if (encAlgo == null) { encAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM); } @@ -658,12 +656,19 @@ private static String getKeyEncryptionAlgo(Message m, Properties props, } // Otherwise check newer identifier - return KeyManagementUtils.getKeyAlgorithm(m, props, - JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM, defaultAlgo); + if (props != null) { + return getKeyEncryptionAlgorithm(props, defaultAlgo); + } } return algo; } - private static String getDefaultKeyAlgo(JsonWebKey jwk) { + public static String getKeyEncryptionAlgorithm(Properties props, String defaultAlgo) { + return KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), + props, + JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM, + defaultAlgo); + } + private static String getDefaultKeyAlgorithm(JsonWebKey jwk) { KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { return AlgorithmUtils.A128GCMKW_ALGO; @@ -704,12 +709,23 @@ private static JweHeaders toJweHeaders(String ct) { return new JweHeaders(Collections.singletonMap(JoseConstants.HEADER_CONTENT_TYPE, ct)); } public static void validateJweCertificateChain(List certs) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, true, - JoseConstants.RSSEC_ENCRYPTION_IN_PROPS, - JoseConstants.RSSEC_ENCRYPTION_PROPS); + Properties props = loadEncryptionInProperties(true); KeyManagementUtils.validateCertificateChain(props, certs); } + public static Properties loadEncryptionInProperties(boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + return KeyManagementUtils.loadStoreProperties(m, required, + JoseConstants.RSSEC_ENCRYPTION_IN_PROPS, + JoseConstants.RSSEC_ENCRYPTION_PROPS); + + } + public static Properties loadEncryptionOutProperties(boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + return KeyManagementUtils.loadStoreProperties(m, required, + JoseConstants.RSSEC_ENCRYPTION_OUT_PROPS, + JoseConstants.RSSEC_ENCRYPTION_PROPS); + + } public static void checkEncryptionKeySize(Key key) { if (key instanceof RSAKey && ((RSAKey)key).getModulus().bitLength() < 2048) { diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java index 5fba6359248..ec14b6be9a8 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java @@ -26,8 +26,6 @@ import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; import org.apache.cxf.message.Message; import org.apache.cxf.phase.PhaseInterceptorChain; -import org.apache.cxf.rs.security.jose.common.JoseConstants; -import org.apache.cxf.rs.security.jose.common.KeyManagementUtils; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; @@ -142,7 +140,7 @@ private void checkAlgorithm() { if (getAlgorithm() == null) { Properties sigProps = getSignatureProperties(); Message m = PhaseInterceptorChain.getCurrentMessage(); - String signatureAlgo = JwsUtils.getSignatureAlgo(m, sigProps, null, null); + String signatureAlgo = JwsUtils.getSignatureAlgorithm(m, sigProps, null, null); if (signatureAlgo != null) { getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.getAlgorithm(signatureAlgo)); } @@ -153,11 +151,8 @@ private void checkAlgorithm() { } } public Properties getSignatureProperties() { - if (signatureProperties == null && PhaseInterceptorChain.getCurrentMessage() != null) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - signatureProperties = KeyManagementUtils.loadStoreProperties(m, false, - JoseConstants.RSSEC_SIGNATURE_OUT_PROPS, - JoseConstants.RSSEC_SIGNATURE_PROPS); + if (signatureProperties == null) { + signatureProperties = JwsUtils.loadSignatureOutProperties(false); } return signatureProperties; diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index 0bce50e39eb..87921085afa 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -65,6 +65,9 @@ public static String sign(PrivateKey key, SignatureAlgorithm algo, String conten public static String sign(PrivateKey key, SignatureAlgorithm algo, String content, String ct) { return sign(getPrivateKeySignatureProvider(key, algo), content, ct); } + public static String sign(String encodedKey, SignatureAlgorithm algo, String content) { + return sign(JoseUtils.decode(encodedKey), algo, content); + } public static String sign(byte[] key, SignatureAlgorithm algo, String content) { return sign(key, algo, content, null); } @@ -75,6 +78,9 @@ public static String verify(PublicKey key, SignatureAlgorithm algo, String conte JwsCompactConsumer jws = verify(getPublicKeySignatureVerifier(key, algo), content); return jws.getDecodedJwsPayload(); } + public static String verify(String encodedKey, SignatureAlgorithm algo, String content) { + return verify(JoseUtils.decode(encodedKey), algo, content); + } public static String verify(byte[] key, SignatureAlgorithm algo, String content) { JwsCompactConsumer jws = verify(getHmacSignatureVerifier(key, algo), content); return jws.getDecodedJwsPayload(); @@ -113,6 +119,9 @@ public static JwsSignatureProvider getPrivateKeySignatureProvider(PrivateKey key return null; } + public static JwsSignatureProvider getHmacSignatureProvider(String encodedKey, SignatureAlgorithm algo) { + return getHmacSignatureProvider(JoseUtils.decode(encodedKey), algo); + } public static JwsSignatureProvider getHmacSignatureProvider(byte[] key, SignatureAlgorithm algo) { if (algo == null) { LOG.warning("No signature algorithm was defined"); @@ -158,6 +167,9 @@ public static JwsSignatureVerifier getPublicKeySignatureVerifier(PublicKey key, return null; } + public static JwsSignatureVerifier getHmacSignatureVerifier(String encodedKey, SignatureAlgorithm algo) { + return getHmacSignatureVerifier(JoseUtils.decode(encodedKey), algo); + } public static JwsSignatureVerifier getHmacSignatureVerifier(byte[] key, SignatureAlgorithm algo) { if (algo == null) { LOG.warning("No signature algorithm was defined"); @@ -188,29 +200,38 @@ public static Map> getJwsJsonSig public static JwsSignatureProvider loadSignatureProvider(boolean required) { return loadSignatureProvider(null, required); } + public static JwsSignatureProvider loadSignatureProvider(JwsHeaders headers, boolean required) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, required, - JoseConstants.RSSEC_SIGNATURE_OUT_PROPS, - JoseConstants.RSSEC_SIGNATURE_PROPS); + Properties props = loadSignatureOutProperties(required); if (props == null) { return null; } - JwsSignatureProvider theSigProvider = loadSignatureProvider(m, props, headers, false); + JwsSignatureProvider theSigProvider = loadSignatureProvider(props, headers); if (headers != null) { headers.setSignatureAlgorithm(theSigProvider.getAlgorithm()); } return theSigProvider; } + public static Properties loadSignatureOutProperties(boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + return KeyManagementUtils.loadStoreProperties(m, required, + JoseConstants.RSSEC_SIGNATURE_OUT_PROPS, + JoseConstants.RSSEC_SIGNATURE_PROPS); + + } + public static Properties loadSignatureInProperties(boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + return KeyManagementUtils.loadStoreProperties(m, required, + JoseConstants.RSSEC_SIGNATURE_IN_PROPS, + JoseConstants.RSSEC_SIGNATURE_PROPS); + + } public static JwsSignatureVerifier loadSignatureVerifier(boolean required) { return loadSignatureVerifier(null, required); } public static JwsSignatureVerifier loadSignatureVerifier(JwsHeaders headers, boolean required) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, required, - JoseConstants.RSSEC_SIGNATURE_IN_PROPS, - JoseConstants.RSSEC_SIGNATURE_PROPS); - return loadSignatureVerifier(m, props, headers, false); + Properties props = loadSignatureInProperties(required); + return loadSignatureVerifier(props, headers); } public static List loadSignatureProviders(String propLoc, Message m) { Properties props = loadJwsProperties(m, propLoc); @@ -261,10 +282,15 @@ public static boolean validateCriticalHeaders(JoseHeaders headers) { //TODO: validate JWS specific constraints return JoseUtils.validateCriticalHeaders(headers); } + public static JwsSignatureProvider loadSignatureProvider(Properties props, + JoseHeaders headers) { + return loadSignatureProvider(PhaseInterceptorChain.getCurrentMessage(), + props, headers, false); + } public static JwsSignatureProvider loadSignatureProvider(Message m, - Properties props, - JoseHeaders headers, - boolean ignoreNullProvider) { + Properties props, + JoseHeaders headers, + boolean ignoreNullProvider) { JwsSignatureProvider theSigProvider = null; boolean includeCert = headers != null && MessageUtils.getContextualBoolean( @@ -275,7 +301,7 @@ public static JwsSignatureProvider loadSignatureProvider(Message m, if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.SIGN); if (jwk != null) { - String signatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); + String signatureAlgo = getSignatureAlgorithm(m, props, jwk.getAlgorithm(), getDefaultKeyAlgorithm(jwk)); theSigProvider = JwsUtils.getSignatureProvider(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); boolean includePublicKey = headers != null && MessageUtils.getContextualBoolean( @@ -300,7 +326,7 @@ public static JwsSignatureProvider loadSignatureProvider(Message m, } } } else { - String signatureAlgo = getSignatureAlgo(m, props, null, null); + String signatureAlgo = getSignatureAlgorithm(m, props, null, null); if (SignatureAlgorithm.getAlgorithm(signatureAlgo) == SignatureAlgorithm.NONE) { theSigProvider = new NoneJwsSignatureProvider(); } else { @@ -324,7 +350,12 @@ public static JwsSignatureProvider loadSignatureProvider(Message m, } return theSigProvider; } - private static JwsSignatureVerifier loadSignatureVerifier(Message m, + public static JwsSignatureVerifier loadSignatureVerifier(Properties props, + JwsHeaders inHeaders) { + return loadSignatureVerifier(PhaseInterceptorChain.getCurrentMessage(), + props, inHeaders, false); + } + public static JwsSignatureVerifier loadSignatureVerifier(Message m, Properties props, JwsHeaders inHeaders, boolean ignoreNullVerifier) { @@ -361,12 +392,12 @@ private static JwsSignatureVerifier loadSignatureVerifier(Message m, if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.VERIFY, inHeaderKid); if (jwk != null) { - String signatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); + String signatureAlgo = getSignatureAlgorithm(m, props, jwk.getAlgorithm(), getDefaultKeyAlgorithm(jwk)); theVerifier = getSignatureVerifier(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); } } else { - String signatureAlgo = getSignatureAlgo(m, props, null, null); + String signatureAlgo = getSignatureAlgorithm(m, props, null, null); if (SignatureAlgorithm.getAlgorithm(signatureAlgo) == SignatureAlgorithm.NONE && SignatureAlgorithm.NONE.getJwaName().equals(inHeaders.getAlgorithm())) { theVerifier = new NoneJwsSignatureVerifier(); @@ -392,14 +423,17 @@ private static Properties loadJwsProperties(Message m, String propLoc) { } @SuppressWarnings("deprecation") - public static String getSignatureAlgo(Message m, Properties props, String algo, String defaultAlgo) { + public static String getSignatureAlgorithm(Message m, Properties props, String algo, String defaultAlgo) { if (algo == null) { if (defaultAlgo == null) { defaultAlgo = AlgorithmUtils.RS_SHA_256_ALGO; } // Check for deprecated identifier first - String sigAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); + String sigAlgo = null; + if (props != null) { + sigAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); + } if (sigAlgo == null && m != null) { sigAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); } @@ -408,12 +442,19 @@ public static String getSignatureAlgo(Message m, Properties props, String algo, } // Otherwise check newer identifier - return KeyManagementUtils.getKeyAlgorithm(m, props, - JoseConstants.RSSEC_SIGNATURE_ALGORITHM, defaultAlgo); + if (props != null) { + return getSignatureAlgorithm(props, defaultAlgo); + } } return algo; } - private static String getDefaultKeyAlgo(JsonWebKey jwk) { + public static String getSignatureAlgorithm(Properties props, String defaultAlgo) { + return KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), + props, + JoseConstants.RSSEC_SIGNATURE_ALGORITHM, + defaultAlgo); + } + private static String getDefaultKeyAlgorithm(JsonWebKey jwk) { KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { return AlgorithmUtils.HMAC_SHA_256_ALGO; @@ -441,10 +482,7 @@ public static String sign(JwsSignatureProvider jwsSig, String content, String ct } public static void validateJwsCertificateChain(List certs) { - Message m = PhaseInterceptorChain.getCurrentMessage(); - Properties props = KeyManagementUtils.loadStoreProperties(m, true, - JoseConstants.RSSEC_SIGNATURE_IN_PROPS, - JoseConstants.RSSEC_SIGNATURE_PROPS); + Properties props = loadSignatureInProperties(true); KeyManagementUtils.validateCertificateChain(props, certs); } public static boolean isPayloadUnencoded(JwsHeaders jwsHeaders) { From d7a0fb520c122363592dd437e18b1e90561e0386 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 11 Nov 2015 13:43:46 +0000 Subject: [PATCH 0040/1346] Making getting the signature key type more type safe --- .../security/jose/jws/JwsCompactProducer.java | 4 +- .../cxf/rs/security/jose/jws/JwsUtils.java | 54 +++++++++++-------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java index ec14b6be9a8..5ef150a6ffc 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java @@ -140,9 +140,9 @@ private void checkAlgorithm() { if (getAlgorithm() == null) { Properties sigProps = getSignatureProperties(); Message m = PhaseInterceptorChain.getCurrentMessage(); - String signatureAlgo = JwsUtils.getSignatureAlgorithm(m, sigProps, null, null); + SignatureAlgorithm signatureAlgo = JwsUtils.getSignatureAlgorithm(m, sigProps, null, null); if (signatureAlgo != null) { - getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.getAlgorithm(signatureAlgo)); + getJwsHeaders().setSignatureAlgorithm(signatureAlgo); } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index 87921085afa..db12142ee9c 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -301,8 +301,11 @@ public static JwsSignatureProvider loadSignatureProvider(Message m, if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.SIGN); if (jwk != null) { - String signatureAlgo = getSignatureAlgorithm(m, props, jwk.getAlgorithm(), getDefaultKeyAlgorithm(jwk)); - theSigProvider = JwsUtils.getSignatureProvider(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); + SignatureAlgorithm signatureAlgo = getSignatureAlgorithm(m, + props, + SignatureAlgorithm.getAlgorithm(jwk.getAlgorithm()), + getDefaultKeyAlgorithm(jwk)); + theSigProvider = JwsUtils.getSignatureProvider(jwk, signatureAlgo); boolean includePublicKey = headers != null && MessageUtils.getContextualBoolean( m, JoseConstants.RSSEC_SIGNATURE_INCLUDE_PUBLIC_KEY, false); @@ -310,7 +313,7 @@ public static JwsSignatureProvider loadSignatureProvider(Message m, m, JoseConstants.RSSEC_SIGNATURE_INCLUDE_KEY_ID, false); if (includeCert) { - JwkUtils.includeCertChain(jwk, headers, signatureAlgo); + JwkUtils.includeCertChain(jwk, headers, signatureAlgo.getJwaName()); } if (includeCertSha1) { String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); @@ -319,20 +322,19 @@ public static JwsSignatureProvider loadSignatureProvider(Message m, } } if (includePublicKey) { - JwkUtils.includePublicKey(jwk, headers, signatureAlgo); + JwkUtils.includePublicKey(jwk, headers, signatureAlgo.getJwaName()); } if (includeKeyId && jwk.getKeyId() != null && headers != null) { headers.setKeyId(jwk.getKeyId()); } } } else { - String signatureAlgo = getSignatureAlgorithm(m, props, null, null); - if (SignatureAlgorithm.getAlgorithm(signatureAlgo) == SignatureAlgorithm.NONE) { + SignatureAlgorithm signatureAlgo = getSignatureAlgorithm(m, props, null, null); + if (signatureAlgo == SignatureAlgorithm.NONE) { theSigProvider = new NoneJwsSignatureProvider(); } else { PrivateKey pk = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.SIGN); - theSigProvider = getPrivateKeySignatureProvider(pk, - SignatureAlgorithm.getAlgorithm(signatureAlgo)); + theSigProvider = getPrivateKeySignatureProvider(pk, signatureAlgo); if (includeCert) { headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); } @@ -392,19 +394,21 @@ public static JwsSignatureVerifier loadSignatureVerifier(Message m, if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.VERIFY, inHeaderKid); if (jwk != null) { - String signatureAlgo = getSignatureAlgorithm(m, props, jwk.getAlgorithm(), getDefaultKeyAlgorithm(jwk)); - theVerifier = getSignatureVerifier(jwk, SignatureAlgorithm.getAlgorithm(signatureAlgo)); + SignatureAlgorithm signatureAlgo = getSignatureAlgorithm(m, props, + SignatureAlgorithm.getAlgorithm(jwk.getAlgorithm()), + getDefaultKeyAlgorithm(jwk)); + theVerifier = getSignatureVerifier(jwk, signatureAlgo); } } else { - String signatureAlgo = getSignatureAlgorithm(m, props, null, null); - if (SignatureAlgorithm.getAlgorithm(signatureAlgo) == SignatureAlgorithm.NONE + SignatureAlgorithm signatureAlgo = getSignatureAlgorithm(m, props, null, null); + if (signatureAlgo == SignatureAlgorithm.NONE && SignatureAlgorithm.NONE.getJwaName().equals(inHeaders.getAlgorithm())) { theVerifier = new NoneJwsSignatureVerifier(); } else { theVerifier = getPublicKeySignatureVerifier( KeyManagementUtils.loadPublicKey(m, props), - SignatureAlgorithm.getAlgorithm(signatureAlgo)); + signatureAlgo); } } if (theVerifier == null && !ignoreNullVerifier) { @@ -423,10 +427,12 @@ private static Properties loadJwsProperties(Message m, String propLoc) { } @SuppressWarnings("deprecation") - public static String getSignatureAlgorithm(Message m, Properties props, String algo, String defaultAlgo) { + public static SignatureAlgorithm getSignatureAlgorithm(Message m, Properties props, + SignatureAlgorithm algo, + SignatureAlgorithm defaultAlgo) { if (algo == null) { if (defaultAlgo == null) { - defaultAlgo = AlgorithmUtils.RS_SHA_256_ALGO; + defaultAlgo = SignatureAlgorithm.RS256; } // Check for deprecated identifier first @@ -438,7 +444,7 @@ public static String getSignatureAlgorithm(Message m, Properties props, String a sigAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_SIGNATURE_ALGORITHM); } if (sigAlgo != null) { - return sigAlgo; + return SignatureAlgorithm.getAlgorithm(sigAlgo); } // Otherwise check newer identifier @@ -448,20 +454,22 @@ public static String getSignatureAlgorithm(Message m, Properties props, String a } return algo; } - public static String getSignatureAlgorithm(Properties props, String defaultAlgo) { - return KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), + public static SignatureAlgorithm getSignatureAlgorithm(Properties props, + SignatureAlgorithm defaultAlgo) { + String algo = KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, JoseConstants.RSSEC_SIGNATURE_ALGORITHM, - defaultAlgo); + defaultAlgo == null ? null : defaultAlgo.getJwaName()); + return SignatureAlgorithm.getAlgorithm(algo); } - private static String getDefaultKeyAlgorithm(JsonWebKey jwk) { + private static SignatureAlgorithm getDefaultKeyAlgorithm(JsonWebKey jwk) { KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { - return AlgorithmUtils.HMAC_SHA_256_ALGO; + return SignatureAlgorithm.HS256; } else if (KeyType.EC == keyType) { - return AlgorithmUtils.ES_SHA_256_ALGO; + return SignatureAlgorithm.ES256; } else { - return AlgorithmUtils.RS_SHA_256_ALGO; + return SignatureAlgorithm.RS256; } } public static JwsCompactConsumer verify(JwsSignatureVerifier v, String content) { From 13eeb07168aecc9098d3c11d9aab6ef21cd62780 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 11 Nov 2015 13:56:10 +0000 Subject: [PATCH 0041/1346] Adding an npe guard to KeyManagementUtils.getKeyAlgorithm --- .../apache/cxf/rs/security/jose/common/KeyManagementUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java index 2ca6e805849..a0bfdf8d60d 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -315,7 +315,7 @@ public static X509Certificate[] toX509CertificateChainArray(List base64E return chain == null ? null : chain.toArray(new X509Certificate[]{}); } public static String getKeyAlgorithm(Message m, Properties props, String propName, String defaultAlg) { - String algo = props.getProperty(propName); + String algo = props != null ? props.getProperty(propName) : null; if (algo == null && m != null) { algo = (String)m.getContextualProperty(propName); } From 030fd7f4d1225e17d2cb91f58b9b8a2625bc5779 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 11 Nov 2015 14:38:59 +0000 Subject: [PATCH 0042/1346] Removing the leftover code in OAuth2 jwt reader --- .../oauth2/provider/AbstractOAuthJoseJwtConsumer.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java index b69d934f44c..42a66defc69 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java @@ -59,15 +59,9 @@ protected JweDecryptionProvider getInitializedDecryptionProvider(String clientSe } public void setDecryptWithClientSecret(boolean decryptWithClientSecret) { - if (verifyWithClientSecret) { - throw new SecurityException(); - } this.decryptWithClientSecret = verifyWithClientSecret; } public void setVerifyWithClientSecret(boolean verifyWithClientSecret) { - if (verifyWithClientSecret) { - throw new SecurityException(); - } this.verifyWithClientSecret = verifyWithClientSecret; } } From ac1dbc4984cb86a0deda1db79899ff20471c9c97 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 11 Nov 2015 16:40:40 +0000 Subject: [PATCH 0043/1346] Making HS algo configurable when verifying with a client secret --- .../rs/security/jose/jwa/AlgorithmUtils.java | 21 ++++++++++++++++--- .../AbstractOAuthJoseJwtConsumer.java | 10 +++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java index 76854cac1ec..0145b5d028f 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java @@ -221,9 +221,6 @@ public static boolean isAesGcm(String algo) { public static boolean isAesCbcHmac(String algo) { return ACBC_HS_SET.contains(algo); } - public static boolean isHmacSign(String algo) { - return HMAC_SIGN_SET.contains(algo); - } public static boolean isOctet(String algo) { return isHmacSign(algo) || isAesCbcHmac(algo) @@ -231,18 +228,36 @@ public static boolean isOctet(String algo) { || isAesGcmKeyWrap(algo) || isAesKeyWrap(algo); } + public static boolean isHmacSign(String algo) { + return HMAC_SIGN_SET.contains(algo); + } + public static boolean isHmacSign(SignatureAlgorithm algo) { + return isHmacSign(algo.getJwaName()); + } public static boolean isRsaSign(String algo) { return isRsaShaSign(algo) || isRsaShaPsSign(algo); } + public static boolean isRsaSign(SignatureAlgorithm algo) { + return isRsaSign(algo.getJwaName()); + } public static boolean isRsaShaSign(String algo) { return RSA_SHA_SIGN_SET.contains(algo); } + public static boolean isRsaShaSign(SignatureAlgorithm algo) { + return isRsaShaSign(algo.getJwaName()); + } public static boolean isRsaShaPsSign(String algo) { return RSA_SHA_PS_SIGN_SET.contains(algo); } + public static boolean isRsaShaPsSign(SignatureAlgorithm algo) { + return isRsaShaPsSign(algo.getJwaName()); + } public static boolean isEcDsaSign(String algo) { return EC_SHA_SIGN_SET.contains(algo); } + public static boolean isEcDsaSign(SignatureAlgorithm algo) { + return isEcDsaSign(algo.getJwaName()); + } public static String toJwaName(String javaName, int keyBitSize) { //TODO: perhaps a key should be a name+keysize pair diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java index 42a66defc69..e799e35fdc7 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java @@ -18,8 +18,11 @@ */ package org.apache.cxf.rs.security.oauth2.provider; +import java.util.Properties; + import javax.crypto.SecretKey; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; @@ -43,8 +46,11 @@ protected JwtToken getJwtToken(String wrappedJwtToken, String clientSecret) { protected JwsSignatureVerifier getInitializedSignatureVerifier(String clientSecret) { if (verifyWithClientSecret) { - byte[] hmac = CryptoUtils.decodeSequence(clientSecret); - return JwsUtils.getHmacSignatureVerifier(hmac, SignatureAlgorithm.HS256); + Properties props = JwsUtils.loadSignatureInProperties(false); + SignatureAlgorithm sigAlgo = JwsUtils.getSignatureAlgorithm(props, SignatureAlgorithm.HS256); + if (AlgorithmUtils.isHmacSign(sigAlgo)) { + return JwsUtils.getHmacSignatureVerifier(clientSecret, sigAlgo); + } } return null; } From fc54f21168a9294f2900bd6bc30d1b2eb5a172e7 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 11 Nov 2015 15:02:59 +0000 Subject: [PATCH 0044/1346] Refactoring how tokens are encrypted in the STS --- .../cxf/sts/operation/AbstractOperation.java | 125 +--------------- .../sts/operation/TokenIssueOperation.java | 16 +-- .../sts/operation/TokenRenewOperation.java | 11 +- .../sts/token/provider/SAMLTokenProvider.java | 11 +- .../cxf/sts/token/provider/SCTProvider.java | 14 +- .../provider/TokenProviderParameters.java | 9 ++ .../token/provider/TokenProviderUtils.java | 135 ++++++++++++++++++ .../cxf/sts/operation/DummyTokenProvider.java | 13 ++ 8 files changed, 186 insertions(+), 148 deletions(-) diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/AbstractOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/AbstractOperation.java index d7c2c452d2f..e47287c48cb 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/AbstractOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/AbstractOperation.java @@ -21,9 +21,7 @@ import java.net.URI; import java.security.Principal; -import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; @@ -77,19 +75,12 @@ import org.apache.cxf.ws.security.sts.provider.model.secext.SecurityTokenReferenceType; import org.apache.cxf.ws.security.sts.provider.model.utility.AttributedDateTime; import org.apache.cxf.ws.security.tokenstore.TokenStore; -import org.apache.cxf.ws.security.wss4j.WSS4JUtils; -import org.apache.wss4j.common.WSEncryptionPart; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.common.util.XMLUtils; import org.apache.wss4j.dom.WSConstants; -import org.apache.wss4j.dom.handler.WSHandlerConstants; -import org.apache.wss4j.dom.handler.WSHandlerResult; -import org.apache.wss4j.dom.message.WSSecEncrypt; import org.apache.wss4j.dom.message.WSSecEncryptedKey; import org.apache.wss4j.dom.util.XmlSchemaDateFormat; -import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants; import org.apache.xml.security.exceptions.XMLSecurityException; -import org.apache.xml.security.stax.securityEvent.AbstractSecuredElementSecurityEvent; import org.apache.xml.security.stax.securityEvent.SecurityEvent; import org.apache.xml.security.stax.securityEvent.SecurityEventConstants; import org.apache.xml.security.stax.securityEvent.TokenSecurityEvent; @@ -311,80 +302,6 @@ protected static LifetimeType createLifetime( return lifetimeType; } - /** - * Encrypt a Token element using the given arguments. - */ - protected Element encryptToken( - Element element, - String id, - EncryptionProperties encryptionProperties, - KeyRequirements keyRequirements, - WebServiceContext context - ) throws WSSecurityException { - String name = encryptionProperties.getEncryptionName(); - if (name == null) { - name = stsProperties.getEncryptionUsername(); - } - if (name == null) { - LOG.fine("No encryption alias is configured"); - return element; - } - - // Get the encryption algorithm to use - String encryptionAlgorithm = keyRequirements.getEncryptionAlgorithm(); - if (encryptionAlgorithm == null) { - // If none then default to what is configured - encryptionAlgorithm = encryptionProperties.getEncryptionAlgorithm(); - } else { - List supportedAlgorithms = - encryptionProperties.getAcceptedEncryptionAlgorithms(); - if (!supportedAlgorithms.contains(encryptionAlgorithm)) { - encryptionAlgorithm = encryptionProperties.getEncryptionAlgorithm(); - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("EncryptionAlgorithm not supported, defaulting to: " + encryptionAlgorithm); - } - } - } - // Get the key-wrap algorithm to use - String keyWrapAlgorithm = keyRequirements.getKeywrapAlgorithm(); - if (keyWrapAlgorithm == null) { - // If none then default to what is configured - keyWrapAlgorithm = encryptionProperties.getKeyWrapAlgorithm(); - } else { - List supportedAlgorithms = - encryptionProperties.getAcceptedKeyWrapAlgorithms(); - if (!supportedAlgorithms.contains(keyWrapAlgorithm)) { - keyWrapAlgorithm = encryptionProperties.getKeyWrapAlgorithm(); - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("KeyWrapAlgorithm not supported, defaulting to: " + keyWrapAlgorithm); - } - } - } - - WSSecEncrypt builder = new WSSecEncrypt(); - if (WSHandlerConstants.USE_REQ_SIG_CERT.equals(name)) { - X509Certificate cert = getReqSigCert(context.getMessageContext()); - builder.setUseThisCert(cert); - } else { - builder.setUserInfo(name); - } - builder.setKeyIdentifierType(encryptionProperties.getKeyIdentifierType()); - builder.setSymmetricEncAlgorithm(encryptionAlgorithm); - builder.setKeyEncAlgo(keyWrapAlgorithm); - builder.setEmbedEncryptedKey(true); - - WSEncryptionPart encryptionPart = new WSEncryptionPart(id, "Element"); - encryptionPart.setElement(element); - - Document doc = element.getOwnerDocument(); - doc.appendChild(element); - - builder.prepare(element.getOwnerDocument(), stsProperties.getEncryptionCrypto()); - builder.encryptForRef(null, Collections.singletonList(encryptionPart)); - - return doc.getDocumentElement(); - } - /** * Encrypt a secret using the given arguments producing a DOM EncryptedKey element */ @@ -475,6 +392,7 @@ protected TokenProviderParameters createTokenProviderParameters( providerParameters.setPrincipal(context.getUserPrincipal()); providerParameters.setWebServiceContext(context); providerParameters.setTokenStore(getTokenStore()); + providerParameters.setEncryptToken(encryptIssuedToken); KeyRequirements keyRequirements = requestRequirements.getKeyRequirements(); TokenRequirements tokenRequirements = requestRequirements.getTokenRequirements(); @@ -542,47 +460,6 @@ protected TokenProviderParameters createTokenProviderParameters( return providerParameters; } - /** - * Get the X509Certificate associated with the signature that was received. This cert is to be used - * for encrypting the issued token. - */ - private X509Certificate getReqSigCert(MessageContext context) { - @SuppressWarnings("unchecked") - List results = - (List) context.get(WSHandlerConstants.RECV_RESULTS); - // DOM - X509Certificate cert = WSS4JUtils.getReqSigCert(results); - if (cert != null) { - return cert; - } - - // Streaming - @SuppressWarnings("unchecked") - final List incomingEventList = - (List) context.get(SecurityEvent.class.getName() + ".in"); - if (incomingEventList != null) { - for (SecurityEvent incomingEvent : incomingEventList) { - if (WSSecurityEventConstants.SignedPart == incomingEvent.getSecurityEventType() - || WSSecurityEventConstants.SignedElement - == incomingEvent.getSecurityEventType()) { - org.apache.xml.security.stax.securityToken.SecurityToken token = - ((AbstractSecuredElementSecurityEvent)incomingEvent).getSecurityToken(); - try { - if (token != null && token.getX509Certificates() != null - && token.getX509Certificates().length > 0) { - return token.getX509Certificates()[0]; - } - } catch (XMLSecurityException ex) { - LOG.log(Level.FINE, ex.getMessage(), ex); - return null; - } - } - } - } - - return null; - } - protected TokenValidatorResponse validateReceivedToken( WebServiceContext context, String realm, TokenRequirements tokenRequirements, ReceivedToken token) { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java index 39f5b6b50e1..383535e7339 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java @@ -288,7 +288,10 @@ private RequestSecurityTokenResponseType createResponse( JAXBElement requestedToken = QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(requestedTokenType); LOG.fine("Encrypting Issued Token: " + encryptIssuedToken); - if (!encryptIssuedToken) { + if (encryptIssuedToken) { + requestedTokenType.setAny(tokenResponse.getToken()); + response.getAny().add(requestedToken); + } else { if (tokenResponse.getToken() instanceof String) { Document doc = DOMUtils.newDocument(); Element requestedTokenEl = doc.createElementNS(STSConstants.WST_NS_05_12, @@ -299,17 +302,6 @@ private RequestSecurityTokenResponseType createResponse( requestedTokenType.setAny(tokenResponse.getToken()); response.getAny().add(requestedToken); } - } else { - if (!(tokenResponse.getToken() instanceof Element)) { - throw new STSException("Error in creating the response", STSException.REQUEST_FAILED); - } - requestedTokenType.setAny( - encryptToken( - (Element)tokenResponse.getToken(), tokenResponse.getTokenId(), - encryptionProperties, keyRequirements, webServiceContext - ) - ); - response.getAny().add(requestedToken); } if (returnReferences) { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenRenewOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenRenewOperation.java index e7cba56fb7e..f4815f4eeb4 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenRenewOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenRenewOperation.java @@ -225,16 +225,7 @@ private RequestSecurityTokenResponseType createResponse( JAXBElement requestedToken = QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(requestedTokenType); LOG.fine("Encrypting Issued Token: " + encryptIssuedToken); - if (!encryptIssuedToken) { - requestedTokenType.setAny(tokenRenewerResponse.getToken()); - } else { - requestedTokenType.setAny( - encryptToken( - tokenRenewerResponse.getToken(), tokenRenewerResponse.getTokenId(), - encryptionProperties, keyRequirements, webServiceContext - ) - ); - } + requestedTokenType.setAny(tokenRenewerResponse.getToken()); response.getAny().add(requestedToken); if (returnReferences) { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java index 3d5d76260ea..ad6b38648b6 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java @@ -137,7 +137,7 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters } TokenProviderResponse response = new TokenProviderResponse(); - response.setToken(token); + String tokenType = tokenRequirements.getTokenType(); if (WSConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType) || WSConstants.SAML2_NS.equals(tokenType)) { @@ -146,6 +146,15 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters response.setTokenId(token.getAttributeNS(null, "AssertionID")); } + if (tokenParameters.isEncryptToken()) { + token = TokenProviderUtils.encryptToken(token, response.getTokenId(), + tokenParameters.getStsProperties(), + tokenParameters.getEncryptionProperties(), + keyRequirements, + tokenParameters.getWebServiceContext()); + } + response.setToken(token); + DateTime validFrom = null; DateTime validTill = null; if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)) { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SCTProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SCTProvider.java index c00af455c28..93f3a085e12 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SCTProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SCTProvider.java @@ -26,6 +26,8 @@ import java.util.logging.Logger; import org.w3c.dom.Document; +import org.w3c.dom.Element; + import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.sts.STSConstants; @@ -123,7 +125,6 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters sct.setID(wssConfig.getIdAllocator().createId("sctId-", sct)); TokenProviderResponse response = new TokenProviderResponse(); - response.setToken(sct.getElement()); response.setTokenId(sct.getIdentifier()); if (returnEntropy) { response.setEntropy(keyHandler.getEntropyBytes()); @@ -173,6 +174,17 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters } tokenParameters.getTokenStore().add(token); + + if (tokenParameters.isEncryptToken()) { + Element el = TokenProviderUtils.encryptToken(sct.getElement(), response.getTokenId(), + tokenParameters.getStsProperties(), + tokenParameters.getEncryptionProperties(), + tokenParameters.getKeyRequirements(), + tokenParameters.getWebServiceContext()); + response.setToken(el); + } else { + response.setToken(sct.getElement()); + } // Create the references TokenReference attachedReference = new TokenReference(); diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderParameters.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderParameters.java index 35841b6b237..aeb57982f08 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderParameters.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderParameters.java @@ -52,6 +52,7 @@ public class TokenProviderParameters { private Map additionalProperties; private TokenStore tokenStore; private String realm; + private boolean encryptToken; public TokenStore getTokenStore() { return tokenStore; @@ -156,5 +157,13 @@ public ClaimCollection getRequestedSecondaryClaims() { public void setRequestedSecondaryClaims(ClaimCollection requestedSecondaryClaims) { this.requestedSecondaryClaims = requestedSecondaryClaims; } + + public boolean isEncryptToken() { + return encryptToken; + } + + public void setEncryptToken(boolean encryptToken) { + this.encryptToken = encryptToken; + } } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java index 406c02e10fd..53ef14b2c9f 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/TokenProviderUtils.java @@ -18,17 +18,37 @@ */ package org.apache.cxf.sts.token.provider; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; +import javax.xml.ws.WebServiceContext; +import javax.xml.ws.handler.MessageContext; +import org.w3c.dom.Document; import org.w3c.dom.Element; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; import org.apache.cxf.ws.addressing.EndpointReferenceType; +import org.apache.cxf.ws.security.wss4j.WSS4JUtils; +import org.apache.wss4j.common.WSEncryptionPart; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.dom.handler.WSHandlerConstants; +import org.apache.wss4j.dom.handler.WSHandlerResult; +import org.apache.wss4j.dom.message.WSSecEncrypt; +import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.stax.securityEvent.AbstractSecuredElementSecurityEvent; +import org.apache.xml.security.stax.securityEvent.SecurityEvent; public final class TokenProviderUtils { @@ -79,4 +99,119 @@ public static String extractAddressFromParticipantsEPR(Object participants) { return null; } + /** + * Encrypt a Token element using the given arguments. + */ + public static Element encryptToken( + Element element, + String id, + STSPropertiesMBean stsProperties, + EncryptionProperties encryptionProperties, + KeyRequirements keyRequirements, + WebServiceContext context + ) throws WSSecurityException { + String name = encryptionProperties.getEncryptionName(); + if (name == null) { + name = stsProperties.getEncryptionUsername(); + } + if (name == null) { + LOG.fine("No encryption alias is configured"); + return element; + } + + // Get the encryption algorithm to use + String encryptionAlgorithm = keyRequirements.getEncryptionAlgorithm(); + if (encryptionAlgorithm == null) { + // If none then default to what is configured + encryptionAlgorithm = encryptionProperties.getEncryptionAlgorithm(); + } else { + List supportedAlgorithms = + encryptionProperties.getAcceptedEncryptionAlgorithms(); + if (!supportedAlgorithms.contains(encryptionAlgorithm)) { + encryptionAlgorithm = encryptionProperties.getEncryptionAlgorithm(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("EncryptionAlgorithm not supported, defaulting to: " + encryptionAlgorithm); + } + } + } + // Get the key-wrap algorithm to use + String keyWrapAlgorithm = keyRequirements.getKeywrapAlgorithm(); + if (keyWrapAlgorithm == null) { + // If none then default to what is configured + keyWrapAlgorithm = encryptionProperties.getKeyWrapAlgorithm(); + } else { + List supportedAlgorithms = + encryptionProperties.getAcceptedKeyWrapAlgorithms(); + if (!supportedAlgorithms.contains(keyWrapAlgorithm)) { + keyWrapAlgorithm = encryptionProperties.getKeyWrapAlgorithm(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("KeyWrapAlgorithm not supported, defaulting to: " + keyWrapAlgorithm); + } + } + } + + WSSecEncrypt builder = new WSSecEncrypt(); + if (WSHandlerConstants.USE_REQ_SIG_CERT.equals(name)) { + X509Certificate cert = getReqSigCert(context.getMessageContext()); + builder.setUseThisCert(cert); + } else { + builder.setUserInfo(name); + } + builder.setKeyIdentifierType(encryptionProperties.getKeyIdentifierType()); + builder.setSymmetricEncAlgorithm(encryptionAlgorithm); + builder.setKeyEncAlgo(keyWrapAlgorithm); + builder.setEmbedEncryptedKey(true); + + WSEncryptionPart encryptionPart = new WSEncryptionPart(id, "Element"); + encryptionPart.setElement(element); + + Document doc = element.getOwnerDocument(); + doc.appendChild(element); + + builder.prepare(element.getOwnerDocument(), stsProperties.getEncryptionCrypto()); + builder.encryptForRef(null, Collections.singletonList(encryptionPart)); + + return doc.getDocumentElement(); + } + + /** + * Get the X509Certificate associated with the signature that was received. This cert is to be used + * for encrypting the issued token. + */ + public static X509Certificate getReqSigCert(MessageContext context) { + @SuppressWarnings("unchecked") + List results = + (List) context.get(WSHandlerConstants.RECV_RESULTS); + // DOM + X509Certificate cert = WSS4JUtils.getReqSigCert(results); + if (cert != null) { + return cert; + } + + // Streaming + @SuppressWarnings("unchecked") + final List incomingEventList = + (List) context.get(SecurityEvent.class.getName() + ".in"); + if (incomingEventList != null) { + for (SecurityEvent incomingEvent : incomingEventList) { + if (WSSecurityEventConstants.SignedPart == incomingEvent.getSecurityEventType() + || WSSecurityEventConstants.SignedElement + == incomingEvent.getSecurityEventType()) { + org.apache.xml.security.stax.securityToken.SecurityToken token = + ((AbstractSecuredElementSecurityEvent)incomingEvent).getSecurityToken(); + try { + if (token != null && token.getX509Certificates() != null + && token.getX509Certificates().length > 0) { + return token.getX509Certificates()[0]; + } + } catch (XMLSecurityException ex) { + LOG.log(Level.FINE, ex.getMessage(), ex); + return null; + } + } + } + } + + return null; + } } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/DummyTokenProvider.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/DummyTokenProvider.java index 87b7ea3df88..b8d590f0944 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/DummyTokenProvider.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/DummyTokenProvider.java @@ -20,10 +20,12 @@ package org.apache.cxf.sts.operation; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.TokenProviderUtils; import org.apache.cxf.ws.security.sts.provider.STSException; import org.apache.wss4j.common.token.BinarySecurity; import org.apache.wss4j.dom.WSConstants; @@ -64,6 +66,17 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters response.setToken(bst.getElement()); response.setTokenId(id); + if (tokenParameters.isEncryptToken()) { + Element el = TokenProviderUtils.encryptToken(bst.getElement(), response.getTokenId(), + tokenParameters.getStsProperties(), + tokenParameters.getEncryptionProperties(), + tokenParameters.getKeyRequirements(), + tokenParameters.getWebServiceContext()); + response.setToken(el); + } else { + response.setToken(bst.getElement()); + } + return response; } catch (Exception e) { throw new STSException("Can't serialize SAML assertion", e, STSException.REQUEST_FAILED); From ab05845f33e5744f9ed9c2b3569a1001c269f923 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 11 Nov 2015 16:59:50 +0000 Subject: [PATCH 0045/1346] Make it possible to pass Properties for encryption/decryption --- .../jose/common/KeyManagementUtils.java | 12 +++++++-- .../cxf/rs/security/jose/jwe/JweUtils.java | 25 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java index a0bfdf8d60d..b18295a4579 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -204,11 +204,19 @@ public static PrivateKeyPasswordProvider loadPasswordProvider(Message m, Propert : keyOper == KeyOperation.DECRYPT ? JoseConstants.RSSEC_DECRYPTION_KEY_PSWD_PROVIDER : null; if (propName != null) { - cb = (PrivateKeyPasswordProvider)m.getContextualProperty(propName); + if (props.containsKey(propName)) { + cb = (PrivateKeyPasswordProvider)props.get(propName); + } else if (m != null) { + cb = (PrivateKeyPasswordProvider)m.getContextualProperty(propName); + } } } if (cb == null) { - cb = (PrivateKeyPasswordProvider)m.getContextualProperty(JoseConstants.RSSEC_KEY_PSWD_PROVIDER); + if (props.containsKey(JoseConstants.RSSEC_KEY_PSWD_PROVIDER)) { + cb = (PrivateKeyPasswordProvider)props.get(JoseConstants.RSSEC_KEY_PSWD_PROVIDER); + } else if (m != null) { + cb = (PrivateKeyPasswordProvider)m.getContextualProperty(JoseConstants.RSSEC_KEY_PSWD_PROVIDER); + } } return cb; } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 074dfa5e0be..95ebcb492a6 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -285,12 +285,17 @@ public static JweDecryption getDirectKeyJweDecryption(SecretKey key, ContentAlgo public static JweEncryptionProvider loadEncryptionProvider(boolean required) { return loadEncryptionProvider(null, required); } - @SuppressWarnings("deprecation") + public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, boolean required) { Properties props = loadEncryptionOutProperties(required); if (props == null) { return null; } + return loadEncryptionProvider(props, headers, required); + } + + @SuppressWarnings("deprecation") + public static JweEncryptionProvider loadEncryptionProvider(Properties props, JweHeaders headers, boolean required) { Message m = PhaseInterceptorChain.getCurrentMessage(); boolean includeCert = @@ -303,7 +308,9 @@ public static JweEncryptionProvider loadEncryptionProvider(JweHeaders headers, b String keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, null, null); KeyAlgorithm keyAlgo = KeyAlgorithm.getAlgorithm(keyEncryptionAlgo); String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); - m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentEncryptionAlgo); + if (m != null) { + m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentEncryptionAlgo); + } ContentEncryptionProvider ctEncryptionProvider = null; if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.ENCRYPT); @@ -367,7 +374,15 @@ public static JweDecryptionProvider loadDecryptionProvider(JweHeaders inHeaders, Properties props = loadEncryptionInProperties(required); if (props == null) { return null; - } + } + + return loadDecryptionProvider(props, inHeaders, required); + } + + public static JweDecryptionProvider loadDecryptionProvider(Properties props, + JweHeaders inHeaders, + boolean required) { + Message m = PhaseInterceptorChain.getCurrentMessage(); KeyDecryptionProvider keyDecryptionProvider = null; String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); @@ -648,7 +663,7 @@ public static String getKeyEncryptionAlgorithm(Message m, Properties props, if (props != null) { encAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM); } - if (encAlgo == null) { + if (encAlgo == null && m != null) { encAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM); } if (encAlgo != null) { @@ -681,7 +696,7 @@ private static String getContentEncryptionAlgo(Message m, Properties props, Stri if (algo == null) { // Check for deprecated identifier first String encAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_CONTENT_ALGORITHM); - if (encAlgo == null) { + if (encAlgo == null && m != null) { encAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_CONTENT_ALGORITHM); } if (encAlgo != null) { From b297eed6d676eb0e5ee3fa4f597d04e1fb9652ed Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 11 Nov 2015 17:17:46 +0000 Subject: [PATCH 0046/1346] Add the ability to encrypt JWT tokens from the STS --- .../token/provider/jwt/JWTTokenProvider.java | 77 ++++++++++- .../token/provider/JWTTokenProviderTest.java | 125 +++++++++++++++++- 2 files changed, 195 insertions(+), 7 deletions(-) diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java index 5afffda1a51..54a4c4e7d7e 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -31,10 +31,16 @@ import javax.security.auth.callback.CallbackHandler; import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.message.Message; import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; +import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweHeaders; +import org.apache.cxf.rs.security.jose.jwe.JweUtils; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; import org.apache.cxf.rs.security.jose.jws.JwsUtils; @@ -43,7 +49,9 @@ import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.SignatureProperties; import org.apache.cxf.sts.cache.CacheUtils; +import org.apache.cxf.sts.request.KeyRequirements; import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; @@ -113,8 +121,13 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters try { JwtToken token = new JwtToken(claims); - String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties(), - tokenParameters.getTokenRequirements()); + String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties()); + if (tokenParameters.isEncryptToken()) { + tokenData = encryptToken(tokenData, token.getJweHeaders(), + tokenParameters.getStsProperties(), + tokenParameters.getEncryptionProperties(), + tokenParameters.getKeyRequirements()); + } TokenProviderResponse response = new TokenProviderResponse(); response.setToken(tokenData); @@ -194,8 +207,7 @@ public void setJwtClaimsProvider(JWTClaimsProvider jwtClaimsProvider) { private String signToken( JwtToken token, RealmProperties jwtRealm, - STSPropertiesMBean stsProperties, - TokenRequirements tokenRequirements + STSPropertiesMBean stsProperties ) throws Exception { Properties signingProperties = new Properties(); @@ -277,5 +289,62 @@ private String signToken( } } + + private String encryptToken( + String token, + JweHeaders jweHeaders, + STSPropertiesMBean stsProperties, + EncryptionProperties encryptionProperties, + KeyRequirements keyRequirements + ) throws Exception { + + Properties encProperties = new Properties(); + + String name = encryptionProperties.getEncryptionName(); + if (name == null) { + name = stsProperties.getEncryptionUsername(); + } + if (name == null) { + LOG.fine("No encryption alias is configured"); + return token; + } + encProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, name); + + // Get the encryption algorithm to use - for now we don't allow the client to ask + // for a particular encryption algorithm, as with SAML + String encryptionAlgorithm = encryptionProperties.getEncryptionAlgorithm(); + try { + ContentAlgorithm.getAlgorithm(encryptionAlgorithm); + } catch (IllegalArgumentException ex) { + encryptionAlgorithm = ContentAlgorithm.A128GCM.name(); + } + encProperties.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, encryptionAlgorithm); + + // Get the key-wrap algorithm to use - for now we don't allow the client to ask + // for a particular encryption algorithm, as with SAML + String keyWrapAlgorithm = encryptionProperties.getKeyWrapAlgorithm(); + try { + KeyAlgorithm.getAlgorithm(keyWrapAlgorithm); + } catch (IllegalArgumentException ex) { + keyWrapAlgorithm = KeyAlgorithm.RSA_OAEP.name(); + } + encProperties.put(JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM, keyWrapAlgorithm); + + // Initialise encryption objects with defaults of STSPropertiesMBean + Crypto encryptionCrypto = stsProperties.getEncryptionCrypto(); + + if (!(encryptionCrypto instanceof Merlin)) { + throw new STSException("Can't get the keystore", STSException.REQUEST_FAILED); + } + KeyStore keystore = ((Merlin)encryptionCrypto).getKeyStore(); + encProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); + + JweEncryptionProvider encProvider = + JweUtils.loadEncryptionProvider(encProperties, jweHeaders, false); + // token.getJwsHeaders().setSignatureAlgorithm(sigProvider.getAlgorithm()); + + return encProvider.encrypt(StringUtils.toBytesUTF8(token), null); + + } } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java index 6273e0e6f2f..2af75c27533 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.sts.token.provider; +import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Properties; @@ -25,13 +26,19 @@ import org.apache.cxf.jaxws.context.WebServiceContextImpl; import org.apache.cxf.jaxws.context.WrappedMessageContext; import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.common.JoseConstants; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwe.JweUtils; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.rs.security.jose.jwt.JwtToken; import org.apache.cxf.sts.StaticSTSProperties; import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.common.TestUtils; import org.apache.cxf.sts.request.KeyRequirements; import org.apache.cxf.sts.request.TokenRequirements; import org.apache.cxf.sts.service.EncryptionProperties; @@ -41,6 +48,7 @@ import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.crypto.CryptoType; +import org.apache.wss4j.common.crypto.Merlin; import org.apache.wss4j.common.ext.WSSecurityException; import org.apache.wss4j.common.principal.CustomTokenPrincipal; import org.junit.Assert; @@ -49,9 +57,13 @@ * Some unit tests for creating JWTTokens. */ public class JWTTokenProviderTest extends org.junit.Assert { - + private static boolean unrestrictedPoliciesInstalled; private static TokenStore tokenStore = new DefaultInMemoryTokenStore(); + static { + unrestrictedPoliciesInstalled = TestUtils.checkUnrestrictedPoliciesInstalled(); + }; + @org.junit.Test public void testCreateUnsignedJWT() throws Exception { TokenProvider jwtTokenProvider = new JWTTokenProvider(); @@ -147,6 +159,97 @@ public void testCachedSignedJWT() throws Exception { Assert.assertNotNull(secToken); } + @org.junit.Test + public void testCreateUnsignedEncryptedJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(false); + + TokenProviderParameters providerParameters = createProviderParameters(); + providerParameters.setEncryptToken(true); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 5); + + if (unrestrictedPoliciesInstalled) { + // Validate the token + JweJwtCompactConsumer jwtConsumer = new JweJwtCompactConsumer(token); + Properties decProperties = new Properties(); + Crypto decryptionCrypto = CryptoFactory.getInstance(getDecryptionProperties()); + KeyStore keystore = ((Merlin)decryptionCrypto).getKeyStore(); + decProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); + decProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, "myservicekey"); + decProperties.put(JoseConstants.RSSEC_KEY_PSWD, "skpass"); + + JweDecryptionProvider decProvider = + JweUtils.loadDecryptionProvider(decProperties, jwtConsumer.getHeaders(), false); + + JweDecryptionOutput decOutput = decProvider.decrypt(token); + String decToken = decOutput.getContentText(); + + JwsJwtCompactConsumer jwtJwsConsumer = new JwsJwtCompactConsumer(decToken); + JwtToken jwt = jwtJwsConsumer.getJwtToken(); + + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + } + + } + + @org.junit.Test + public void testCreateSignedEncryptedJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + + TokenProviderParameters providerParameters = createProviderParameters(); + providerParameters.setEncryptToken(true); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 5); + + if (unrestrictedPoliciesInstalled) { + // Validate the token + JweJwtCompactConsumer jwtConsumer = new JweJwtCompactConsumer(token); + Properties decProperties = new Properties(); + Crypto decryptionCrypto = CryptoFactory.getInstance(getDecryptionProperties()); + KeyStore keystore = ((Merlin)decryptionCrypto).getKeyStore(); + decProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); + decProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, "myservicekey"); + decProperties.put(JoseConstants.RSSEC_KEY_PSWD, "skpass"); + + JweDecryptionProvider decProvider = + JweUtils.loadDecryptionProvider(decProperties, jwtConsumer.getHeaders(), false); + + JweDecryptionOutput decOutput = decProvider.decrypt(token); + String decToken = decOutput.getContentText(); + + JwsJwtCompactConsumer jwtJwsConsumer = new JwsJwtCompactConsumer(decToken); + JwtToken jwt = jwtJwsConsumer.getJwtToken(); + + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + } + + } + private TokenProviderParameters createProviderParameters() throws WSSecurityException { TokenProviderParameters parameters = new TokenProviderParameters(); @@ -178,6 +281,9 @@ private TokenProviderParameters createProviderParameters() throws WSSecurityExce parameters.setStsProperties(stsProperties); parameters.setEncryptionProperties(new EncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); return parameters; } @@ -188,11 +294,24 @@ private Properties getEncryptionProperties() { "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" ); properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); - properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + if (unrestrictedPoliciesInstalled) { + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + } else { + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "restricted/stsstore.jks"); + } return properties; } - + private Properties getDecryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "sspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "servicestore.jks"); + + return properties; + } } From 2cfc6a944bdb2050ca38bea1961ac0b32f9e70c8 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 12 Nov 2015 12:13:29 +0000 Subject: [PATCH 0047/1346] Also making HS algo configurable when signing with a client secret --- .../oauth2/provider/AbstractOAuthJoseJwtProducer.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java index e5bf0128a4d..1bd78fe1e6b 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java @@ -18,8 +18,11 @@ */ package org.apache.cxf.rs.security.oauth2.provider; +import java.util.Properties; + import javax.crypto.SecretKey; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; @@ -42,8 +45,11 @@ protected String processJwt(JwtToken jwt, String clientSecret) { protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecret) { if (signWithClientSecret) { - byte[] hmac = CryptoUtils.decodeSequence(clientSecret); - return JwsUtils.getHmacSignatureProvider(hmac, SignatureAlgorithm.HS256); + Properties props = JwsUtils.loadSignatureOutProperties(false); + SignatureAlgorithm sigAlgo = JwsUtils.getSignatureAlgorithm(props, SignatureAlgorithm.HS256); + if (AlgorithmUtils.isHmacSign(sigAlgo)) { + return JwsUtils.getHmacSignatureProvider(clientSecret, SignatureAlgorithm.HS256); + } } return null; } From cd9d69a3c92e2c23b85e44bfbfe973c029870e78 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 12 Nov 2015 10:40:12 +0000 Subject: [PATCH 0048/1346] Adding more sig/enc tests for JWT tokens in the STS --- .../token/provider/JWTTokenProviderTest.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java index 2af75c27533..51ef2102909 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java @@ -27,6 +27,7 @@ import org.apache.cxf.jaxws.context.WrappedMessageContext; import org.apache.cxf.message.MessageImpl; import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; @@ -35,6 +36,7 @@ import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.SignatureProperties; import org.apache.cxf.sts.StaticSTSProperties; import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; import org.apache.cxf.sts.common.PasswordCallbackHandler; @@ -127,6 +129,46 @@ public void testCreateSignedJWT() throws Exception { assertTrue(jwtConsumer.verifySignatureWith(certs[0], SignatureAlgorithm.RS256)); } + @org.junit.Test + public void testCreateSignedPSJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + TokenProviderParameters providerParameters = createProviderParameters(); + SignatureProperties sigProps = new SignatureProperties(); + sigProps.setSignatureAlgorithm(SignatureAlgorithm.PS256.name()); + providerParameters.getStsProperties().setSignatureProperties(sigProps); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + + // Verify Signature + Crypto crypto = providerParameters.getStsProperties().getSignatureCrypto(); + CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); + cryptoType.setAlias(providerParameters.getStsProperties().getSignatureUsername()); + X509Certificate[] certs = crypto.getX509Certificates(cryptoType); + assertNotNull(certs); + + assertFalse(jwtConsumer.verifySignatureWith(certs[0], SignatureAlgorithm.RS256)); + assertTrue(jwtConsumer.verifySignatureWith(certs[0], SignatureAlgorithm.PS256)); + } + @org.junit.Test public void testCachedSignedJWT() throws Exception { TokenProvider jwtTokenProvider = new JWTTokenProvider(); @@ -205,6 +247,57 @@ public void testCreateUnsignedEncryptedJWT() throws Exception { } + @org.junit.Test + public void testCreateUnsignedEncryptedCBCJWT() throws Exception { + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(false); + + TokenProviderParameters providerParameters = createProviderParameters(); + providerParameters.setEncryptToken(true); + providerParameters.getEncryptionProperties().setEncryptionAlgorithm( + ContentAlgorithm.A128CBC_HS256.name() + ); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 5); + + if (unrestrictedPoliciesInstalled) { + // Validate the token + JweJwtCompactConsumer jwtConsumer = new JweJwtCompactConsumer(token); + Properties decProperties = new Properties(); + Crypto decryptionCrypto = CryptoFactory.getInstance(getDecryptionProperties()); + KeyStore keystore = ((Merlin)decryptionCrypto).getKeyStore(); + decProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); + decProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, "myservicekey"); + decProperties.put(JoseConstants.RSSEC_KEY_PSWD, "skpass"); + decProperties.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, + ContentAlgorithm.A128CBC_HS256.name()); + + JweDecryptionProvider decProvider = + JweUtils.loadDecryptionProvider(decProperties, jwtConsumer.getHeaders(), false); + + JweDecryptionOutput decOutput = decProvider.decrypt(token); + String decToken = decOutput.getContentText(); + + JwsJwtCompactConsumer jwtJwsConsumer = new JwsJwtCompactConsumer(decToken); + JwtToken jwt = jwtJwsConsumer.getJwtToken(); + + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID)); + Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); + Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, + jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); + } + + } + @org.junit.Test public void testCreateSignedEncryptedJWT() throws Exception { TokenProvider jwtTokenProvider = new JWTTokenProvider(); From 822d8f155eeeb3f63ebeb7ec743ad8cf4d222771 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 12 Nov 2015 11:08:23 +0000 Subject: [PATCH 0049/1346] [CXF-6673] - StaticService setEndpoints(List) doesn't work correctly --- .../src/main/java/org/apache/cxf/sts/service/StaticService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/service/StaticService.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/service/StaticService.java index 01809395cd6..3d18550e3fa 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/service/StaticService.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/service/StaticService.java @@ -104,6 +104,7 @@ public void setKeyType(String keyType) { * Set the list of endpoint addresses that correspond to this service */ public void setEndpoints(List endpoints) { + endpointPatterns.clear(); if (endpoints != null) { for (String endpoint : endpoints) { try { From 1960703149a11052490cf16ec3682408470298f9 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 12 Nov 2015 12:14:01 +0000 Subject: [PATCH 0050/1346] Strip out any query parameters when sending the applies to address --- .../cxf/ws/security/trust/STSTokenRetriever.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenRetriever.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenRetriever.java index 3b57bdaeee9..41556a7d7be 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenRetriever.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenRetriever.java @@ -136,9 +136,16 @@ private static SecurityToken issueToken(Message message, TokenRequestParams para Object o = SecurityUtils.getSecurityPropertyValue(SecurityConstants.STS_APPLIES_TO, message); String appliesTo = o == null ? null : o.toString(); - appliesTo = appliesTo == null - ? message.getContextualProperty(Message.ENDPOINT_ADDRESS).toString() - : appliesTo; + if (appliesTo == null) { + String endpointAddress = + message.getContextualProperty(Message.ENDPOINT_ADDRESS).toString(); + // Strip out any query parameters if they exist + int query = endpointAddress.indexOf('?'); + if (query > 0) { + endpointAddress = endpointAddress.substring(0, query); + } + appliesTo = endpointAddress; + } boolean enableAppliesTo = client.isEnableAppliesTo(); client.setMessage(message); From dd598c4985d8f61fd3d4fa7754d936cc6651bcee Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 12 Nov 2015 12:43:34 +0000 Subject: [PATCH 0051/1346] Also trying to make JWE key algo retrieval more type safe --- .../rs/security/jose/jwa/KeyAlgorithm.java | 4 +- .../cxf/rs/security/jose/jwe/JweUtils.java | 68 ++++++++++--------- .../jose/jwe/JweJsonProducerTest.java | 2 +- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/KeyAlgorithm.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/KeyAlgorithm.java index 46bccf3e66f..5a89b0d8024 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/KeyAlgorithm.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/KeyAlgorithm.java @@ -38,7 +38,9 @@ public enum KeyAlgorithm { ECDH_ES_A128KW(AlgorithmUtils.ECDH_ES_A128KW_ALGO, "AESWrap", 128), ECDH_ES_A192KW(AlgorithmUtils.ECDH_ES_A192KW_ALGO, "AESWrap", 192), ECDH_ES_A256KW(AlgorithmUtils.ECDH_ES_A256KW_ALGO, "AESWrap", 256), - ECDH_ES_DIRECT(AlgorithmUtils.ECDH_ES_DIRECT_ALGO, null, -1); + ECDH_ES_DIRECT(AlgorithmUtils.ECDH_ES_DIRECT_ALGO, null, -1), + + DIRECT("direct", null, -1); private final String jwaName; private final String javaName; diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 95ebcb492a6..191a8a72168 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -212,27 +212,28 @@ public static KeyDecryptionProvider getSecretKeyDecryptionProvider(SecretKey key } return null; } - public static ContentEncryptionProvider getContentEncryptionAlgorithm(JsonWebKey jwk) { - return getContentEncryptionAlgorithm(jwk, null); + public static ContentEncryptionProvider getContentEncryptionProvider(JsonWebKey jwk) { + return getContentEncryptionProvider(jwk, null); } - public static ContentEncryptionProvider getContentEncryptionAlgorithm(JsonWebKey jwk, String defaultAlgorithm) { + public static ContentEncryptionProvider getContentEncryptionProvider(JsonWebKey jwk, + String defaultAlgorithm) { String ctEncryptionAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm : jwk.getAlgorithm(); ContentEncryptionProvider contentEncryptionProvider = null; KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { - return getContentEncryptionAlgorithm(JwkUtils.toSecretKey(jwk), + return getContentEncryptionProvider(JwkUtils.toSecretKey(jwk), getContentAlgo(ctEncryptionAlgo)); } return contentEncryptionProvider; } - public static ContentEncryptionProvider getContentEncryptionAlgorithm(SecretKey key, + public static ContentEncryptionProvider getContentEncryptionProvider(SecretKey key, ContentAlgorithm algorithm) { if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) { return new AesGcmContentEncryptionAlgorithm(key, null, algorithm); } return null; } - public static ContentEncryptionProvider getContentEncryptionAlgorithm(String algorithm) { + public static ContentEncryptionProvider getContentEncryptionProvider(String algorithm) { if (AlgorithmUtils.isAesGcm(algorithm)) { return new AesGcmContentEncryptionAlgorithm(getContentAlgo(algorithm)); } @@ -268,7 +269,7 @@ public static JweEncryption getDirectKeyJweEncryption(SecretKey key, ContentAlgo null, new DirectKeyEncryptionAlgorithm()); } else { return new JweEncryption(new DirectKeyEncryptionAlgorithm(), - getContentEncryptionAlgorithm(key, algo)); + getContentEncryptionProvider(key, algo)); } } public static JweDecryption getDirectKeyJweDecryption(JsonWebKey key) { @@ -305,8 +306,7 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1, false); KeyEncryptionProvider keyEncryptionProvider = null; - String keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, null, null); - KeyAlgorithm keyAlgo = KeyAlgorithm.getAlgorithm(keyEncryptionAlgo); + KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null); String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); if (m != null) { m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentEncryptionAlgo); @@ -314,12 +314,13 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe ContentEncryptionProvider ctEncryptionProvider = null; if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.ENCRYPT); - if ("direct".equals(keyEncryptionAlgo)) { + if ("direct".equals(keyAlgo.getJwaName())) { contentEncryptionAlgo = getContentEncryptionAlgo(m, props, jwk.getAlgorithm()); - ctEncryptionProvider = getContentEncryptionAlgorithm(jwk, contentEncryptionAlgo); + ctEncryptionProvider = getContentEncryptionProvider(jwk, contentEncryptionAlgo); } else { - keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, jwk.getAlgorithm(), - getDefaultKeyAlgorithm(jwk)); + keyAlgo = getKeyEncryptionAlgorithm(m, props, + KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), + getDefaultKeyAlgorithm(jwk)); keyEncryptionProvider = getKeyEncryptionProvider(jwk, keyAlgo); boolean includePublicKey = headers != null && MessageUtils.getContextualBoolean( @@ -328,7 +329,7 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_KEY_ID, false); if (includeCert) { - JwkUtils.includeCertChain(jwk, headers, keyEncryptionAlgo); + JwkUtils.includeCertChain(jwk, headers, keyAlgo.getJwaName()); } if (includeCertSha1) { String digest = KeyManagementUtils.loadDigestAndEncodeX509Certificate(m, props); @@ -337,7 +338,7 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe } } if (includePublicKey) { - JwkUtils.includePublicKey(jwk, headers, keyEncryptionAlgo); + JwkUtils.includePublicKey(jwk, headers, keyAlgo.getJwaName()); } if (includeKeyId && jwk.getKeyId() != null && headers != null) { headers.setKeyId(jwk.getKeyId()); @@ -387,7 +388,7 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, KeyDecryptionProvider keyDecryptionProvider = null; String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); SecretKey ctDecryptionKey = null; - String keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, null, null); + KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null); if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { // Supporting loading a private key via a certificate for now List chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); @@ -418,19 +419,19 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE); } - if ("direct".equals(keyEncryptionAlgo)) { + if ("direct".equals(keyAlgo.getJwaName())) { contentEncryptionAlgo = getContentEncryptionAlgo(m, props, jwk.getAlgorithm()); ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentEncryptionAlgo); } else { - keyEncryptionAlgo = getKeyEncryptionAlgorithm(m, props, jwk.getAlgorithm(), - getDefaultKeyAlgorithm(jwk)); - keyDecryptionProvider = getKeyDecryptionProvider(jwk, - KeyAlgorithm.getAlgorithm(keyEncryptionAlgo)); + keyAlgo = getKeyEncryptionAlgorithm(m, props, + KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), + getDefaultKeyAlgorithm(jwk)); + keyDecryptionProvider = getKeyDecryptionProvider(jwk, keyAlgo); } } else { keyDecryptionProvider = getPrivateKeyDecryptionProvider( KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.DECRYPT), - KeyAlgorithm.getAlgorithm(keyEncryptionAlgo)); + keyAlgo); } } return createJweDecryptionProvider(keyDecryptionProvider, ctDecryptionKey, @@ -485,7 +486,7 @@ public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionPro return new AesCbcHmacJweEncryption(getContentAlgo(contentEncryptionAlgo), keyEncryptionProvider); } else { return new JweEncryption(keyEncryptionProvider, - getContentEncryptionAlgorithm(contentEncryptionAlgo)); + getContentEncryptionProvider(contentEncryptionAlgo)); } } public static JweDecryptionProvider createJweDecryptionProvider(PrivateKey key, @@ -651,11 +652,11 @@ private static JweDecryptionProvider createJweDecryptionProvider(KeyDecryptionPr } } @SuppressWarnings("deprecation") - public static String getKeyEncryptionAlgorithm(Message m, Properties props, - String algo, String defaultAlgo) { + public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, + KeyAlgorithm algo, KeyAlgorithm defaultAlgo) { if (algo == null) { if (defaultAlgo == null) { - defaultAlgo = AlgorithmUtils.RSA_OAEP_ALGO; + defaultAlgo = KeyAlgorithm.RSA_OAEP; } // Check for deprecated identifier first @@ -667,7 +668,7 @@ public static String getKeyEncryptionAlgorithm(Message m, Properties props, encAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_KEY_ALGORITHM); } if (encAlgo != null) { - return encAlgo; + return KeyAlgorithm.getAlgorithm(encAlgo); } // Otherwise check newer identifier @@ -677,18 +678,19 @@ public static String getKeyEncryptionAlgorithm(Message m, Properties props, } return algo; } - public static String getKeyEncryptionAlgorithm(Properties props, String defaultAlgo) { - return KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), + public static KeyAlgorithm getKeyEncryptionAlgorithm(Properties props, KeyAlgorithm defaultAlgo) { + String algo = KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM, - defaultAlgo); + defaultAlgo == null ? null : defaultAlgo.getJwaName()); + return algo == null ? null : KeyAlgorithm.getAlgorithm(algo); } - private static String getDefaultKeyAlgorithm(JsonWebKey jwk) { + private static KeyAlgorithm getDefaultKeyAlgorithm(JsonWebKey jwk) { KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { - return AlgorithmUtils.A128GCMKW_ALGO; + return KeyAlgorithm.A128GCMKW; } else { - return AlgorithmUtils.RSA_OAEP_ALGO; + return KeyAlgorithm.RSA_OAEP; } } @SuppressWarnings("deprecation") diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java index 86c142542e7..67d710573a6 100644 --- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java @@ -282,7 +282,7 @@ public void testMultipleRecipients() { KeyEncryptionProvider keyEncryption1 = JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey1, KeyAlgorithm.A128KW); ContentEncryptionProvider contentEncryption = - JweUtils.getContentEncryptionAlgorithm(AlgorithmUtils.A128GCM_ALGO); + JweUtils.getContentEncryptionProvider(AlgorithmUtils.A128GCM_ALGO); JweEncryptionProvider jwe1 = new JweEncryption(keyEncryption1, contentEncryption); KeyEncryptionProvider keyEncryption2 = JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey2, KeyAlgorithm.A128KW); From 88d513e3a47cc52335e9419da00319cf147eb66c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 12 Nov 2015 14:17:36 +0000 Subject: [PATCH 0052/1346] Making it easier to configure Jwe Content algo when using a client secret --- .../cxf/rs/security/jose/jwe/JweUtils.java | 49 ++++++++++++------- .../AbstractOAuthJoseJwtConsumer.java | 4 +- .../AbstractOAuthJoseJwtProducer.java | 6 ++- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 191a8a72168..08e5bf9254f 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -307,16 +307,18 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe KeyEncryptionProvider keyEncryptionProvider = null; KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null); - String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); + ContentAlgorithm contentAlgo = getContentEncryptionAlgorithm(m, props, null, ContentAlgorithm.A128GCM); if (m != null) { - m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentEncryptionAlgo); + m.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, contentAlgo.getJwaName()); } ContentEncryptionProvider ctEncryptionProvider = null; if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, KeyOperation.ENCRYPT); if ("direct".equals(keyAlgo.getJwaName())) { - contentEncryptionAlgo = getContentEncryptionAlgo(m, props, jwk.getAlgorithm()); - ctEncryptionProvider = getContentEncryptionProvider(jwk, contentEncryptionAlgo); + contentAlgo = getContentEncryptionAlgorithm(m, props, + ContentAlgorithm.getAlgorithm(jwk.getAlgorithm()), + ContentAlgorithm.A128GCM); + ctEncryptionProvider = getContentEncryptionProvider(jwk, contentAlgo.getJwaName()); } else { keyAlgo = getKeyEncryptionAlgorithm(m, props, KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), @@ -365,7 +367,7 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe } return createJweEncryptionProvider(keyEncryptionProvider, ctEncryptionProvider, - contentEncryptionAlgo, + contentAlgo.getJwaName(), compression); } public static JweDecryptionProvider loadDecryptionProvider(boolean required) { @@ -386,7 +388,8 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, Message m = PhaseInterceptorChain.getCurrentMessage(); KeyDecryptionProvider keyDecryptionProvider = null; - String contentEncryptionAlgo = getContentEncryptionAlgo(m, props, null); + ContentAlgorithm contentAlgo = + getContentEncryptionAlgorithm(m, props, null, ContentAlgorithm.A128GCM); SecretKey ctDecryptionKey = null; KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null); if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { @@ -396,7 +399,7 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, X509Certificate cert = chain == null ? null : chain.get(0); PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, cert, KeyOperation.DECRYPT); - contentEncryptionAlgo = inHeaders.getContentEncryptionAlgorithm().getJwaName(); + contentAlgo = inHeaders.getContentEncryptionAlgorithm(); keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, inHeaders.getKeyEncryptionAlgorithm()); } else if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_THUMBPRINT) != null) { @@ -407,7 +410,7 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, if (foundCert != null) { PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, foundCert, KeyOperation.DECRYPT); - contentEncryptionAlgo = inHeaders.getContentEncryptionAlgorithm().getJwaName(); + contentAlgo = inHeaders.getContentEncryptionAlgorithm(); keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, inHeaders.getKeyEncryptionAlgorithm()); } @@ -420,8 +423,10 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, } if ("direct".equals(keyAlgo.getJwaName())) { - contentEncryptionAlgo = getContentEncryptionAlgo(m, props, jwk.getAlgorithm()); - ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentEncryptionAlgo); + contentAlgo = getContentEncryptionAlgorithm(m, props, + ContentAlgorithm.getAlgorithm(jwk.getAlgorithm()), + ContentAlgorithm.A128GCM); + ctDecryptionKey = getContentDecryptionSecretKey(jwk, contentAlgo.getJwaName()); } else { keyAlgo = getKeyEncryptionAlgorithm(m, props, KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), @@ -435,7 +440,7 @@ public static JweDecryptionProvider loadDecryptionProvider(Properties props, } } return createJweDecryptionProvider(keyDecryptionProvider, ctDecryptionKey, - getContentAlgo(contentEncryptionAlgo)); + contentAlgo); } public static JweEncryptionProvider createJweEncryptionProvider(PublicKey key, KeyAlgorithm keyAlgo, @@ -694,7 +699,10 @@ private static KeyAlgorithm getDefaultKeyAlgorithm(JsonWebKey jwk) { } } @SuppressWarnings("deprecation") - private static String getContentEncryptionAlgo(Message m, Properties props, String algo) { + public static ContentAlgorithm getContentEncryptionAlgorithm(Message m, + Properties props, + ContentAlgorithm algo, + ContentAlgorithm defaultAlgo) { if (algo == null) { // Check for deprecated identifier first String encAlgo = props.getProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_CONTENT_ALGORITHM); @@ -702,16 +710,23 @@ private static String getContentEncryptionAlgo(Message m, Properties props, Stri encAlgo = (String)m.getContextualProperty(JoseConstants.DEPR_RSSEC_ENCRYPTION_CONTENT_ALGORITHM); } if (encAlgo != null) { - return encAlgo; + return ContentAlgorithm.getAlgorithm(encAlgo); } - // Otherwise check newer identifier - return KeyManagementUtils.getKeyAlgorithm(m, props, - JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, - AlgorithmUtils.A128GCM_ALGO); + return getContentEncryptionAlgorithm(props, defaultAlgo); } return algo; } + + public static ContentAlgorithm getContentEncryptionAlgorithm(Properties props, + ContentAlgorithm defaultAlgo) { + String algo = KeyManagementUtils.getKeyAlgorithm(PhaseInterceptorChain.getCurrentMessage(), + props, + JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, + defaultAlgo == null ? null : defaultAlgo.getJwaName()); + return ContentAlgorithm.getAlgorithm(algo); + } + private static String encrypt(KeyEncryptionProvider keyEncryptionProvider, ContentAlgorithm contentAlgo, byte[] content, String ct) { JweEncryptionProvider jwe = createJweEncryptionProvider(keyEncryptionProvider, contentAlgo, null); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java index e799e35fdc7..5d2fa3bf3d2 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java @@ -58,7 +58,9 @@ protected JweDecryptionProvider getInitializedDecryptionProvider(String clientSe JweDecryptionProvider theDecryptionProvider = null; if (decryptWithClientSecret) { SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); - theDecryptionProvider = JweUtils.getDirectKeyJweDecryption(key, ContentAlgorithm.A128GCM); + Properties props = JweUtils.loadEncryptionInProperties(false); + ContentAlgorithm ctAlgo = JweUtils.getContentEncryptionAlgorithm(props, ContentAlgorithm.A128GCM); + theDecryptionProvider = JweUtils.getDirectKeyJweDecryption(key, ctAlgo); } return theDecryptionProvider; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java index 1bd78fe1e6b..b0a74141296 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java @@ -48,7 +48,7 @@ protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecr Properties props = JwsUtils.loadSignatureOutProperties(false); SignatureAlgorithm sigAlgo = JwsUtils.getSignatureAlgorithm(props, SignatureAlgorithm.HS256); if (AlgorithmUtils.isHmacSign(sigAlgo)) { - return JwsUtils.getHmacSignatureProvider(clientSecret, SignatureAlgorithm.HS256); + return JwsUtils.getHmacSignatureProvider(clientSecret, sigAlgo); } } return null; @@ -56,7 +56,9 @@ protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecr protected JweEncryptionProvider getInitializedEncryptionProvider(String clientSecret) { if (encryptWithClientSecret) { SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); - return JweUtils.getDirectKeyJweEncryption(key, ContentAlgorithm.A128GCM); + Properties props = JweUtils.loadEncryptionOutProperties(false); + ContentAlgorithm ctAlgo = JweUtils.getContentEncryptionAlgorithm(props, ContentAlgorithm.A128GCM); + return JweUtils.getDirectKeyJweEncryption(key, ctAlgo); } return null; } From 1444405de684d0a266cbbfbb415e09121412d577 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Fri, 6 Nov 2015 10:37:40 -0500 Subject: [PATCH 0053/1346] Fix logging feature pom --- rt/features/logging/pom.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/rt/features/logging/pom.xml b/rt/features/logging/pom.xml index 409c69ac04f..1bb6e4f9328 100644 --- a/rt/features/logging/pom.xml +++ b/rt/features/logging/pom.xml @@ -10,7 +10,6 @@ bundle - 3.1.5-SNAPSHOT org.apache.cxf.ext.logging.osgi.Activator @@ -32,7 +31,7 @@ org.apache.cxf cxf-core - ${cxf.version} + ${project.version} org.slf4j @@ -58,25 +57,25 @@ org.apache.cxf cxf-rt-frontend-jaxws - ${cxf.version} + ${project.version} test org.apache.cxf cxf-rt-frontend-jaxrs - ${cxf.version} + ${project.version} test org.apache.cxf cxf-rt-rs-client - ${cxf.version} + ${project.version} test org.apache.cxf cxf-rt-transports-http-jetty - ${cxf.version} + ${project.version} test From a2398cad82a549f9068351baf8185f2a1da4bcb4 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 10:10:14 -0500 Subject: [PATCH 0054/1346] Fix comparison to allow for Jetty 9.3 --- .../test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java index 2dbe1502dcb..f2af1e8d00e 100644 --- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java @@ -124,7 +124,7 @@ private void checkResponseOut(LogEvent responseOut) { private void checkResponseIn(LogEvent responseIn) { Assert.assertNull(responseIn.getAddress()); - Assert.assertEquals("text/xml; charset=UTF-8", responseIn.getContentType()); + Assert.assertEquals("text/xml;charset=utf-8", responseIn.getContentType().toLowerCase().replace(" ", "")); Assert.assertEquals(EventType.RESP_IN, responseIn.getType()); Assert.assertEquals(StandardCharsets.UTF_8.name(), responseIn.getEncoding()); Assert.assertNotNull(responseIn.getExchangeId()); From 2e221ea50c22d528b6b23dd0c0f0cc862e2d0fa4 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 10:10:35 -0500 Subject: [PATCH 0055/1346] Fix setup of SSL with Jetty 9.3 --- .../cxf/transport/http_jetty/JettyHTTPServerEngine.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java b/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java index 0b864f57b7a..e6f0fed301d 100644 --- a/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java +++ b/rt/transports/http-jetty/src/main/java/org/apache/cxf/transport/http_jetty/JettyHTTPServerEngine.java @@ -583,6 +583,7 @@ private Connector createConnector(String hosto, int porto) { protected void doStart() throws Exception { setSslContext(createSSLContext(this)); super.doStart(); + checkKeyStore(); } public void checkKeyStore() { //we'll handle this later @@ -653,8 +654,8 @@ AbstractConnector createConnectorJetty9(SslContextFactory sslcf, String hosto, i String.class) .newInstance(sslcf, "HTTP/1.1"); connectionFactories.add(scf); - String proto = (major > 9 || (major == 9 && minor >= 3)) ? "SSL" : "SSL-HTTP"; - result.getClass().getMethod("setDefaultProtocol", String.class).invoke(result, proto + "/1.1"); + String proto = (major > 9 || (major == 9 && minor >= 3)) ? "SSL" : "SSL-HTTP/1.1"; + result.getClass().getMethod("setDefaultProtocol", String.class).invoke(result, proto); } connectionFactories.add(httpFactory); result.getClass().getMethod("setConnectionFactories", Collection.class) From 3240a6a27e48f41e15ab75ed2c1735a1f5056668 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 10:11:59 -0500 Subject: [PATCH 0056/1346] Fix digest test with Jetty 9.3 --- .../java/org/apache/cxf/systest/http/auth/DigestServer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestServer.java b/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestServer.java index 1571dabbe97..dbc46eda50e 100644 --- a/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestServer.java +++ b/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestServer.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.systest.http.auth; +import java.io.File; import java.net.URISyntaxException; import java.net.URL; @@ -40,8 +41,10 @@ public DigestServer() { protected void configureServer() throws Exception { URL resource = getClass() .getResource("jetty-realm.properties"); + File file = new File(resource.toURI()); + LoginService realm = - new HashLoginService("BookStoreRealm", resource.toString()); + new HashLoginService("BookStoreRealm", file.getAbsolutePath()); server.addBean(realm); } From 29a9b9e96582602565286099ac3965424372370d Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 10:12:34 -0500 Subject: [PATCH 0057/1346] Update poms to use jettys apache-jsp module to prepare for Jetty 9.3 --- systests/cdi/pom.xml | 15 +++++++++++++-- systests/jaxrs/pom.xml | 2 +- systests/jibx/jaxrs-jibx/pom.xml | 2 +- systests/rs-http-sci/pom.xml | 2 +- systests/tracing/pom.xml | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/systests/cdi/pom.xml b/systests/cdi/pom.xml index 187b3d0c59d..4abe4186df1 100644 --- a/systests/cdi/pom.xml +++ b/systests/cdi/pom.xml @@ -35,7 +35,7 @@ 3.0-b01 -XX:MaxPermSize=192m -XX:MaxPermSize=192m - 7.0.52 + 8.0.28 @@ -89,8 +89,14 @@ org.eclipse.jetty - jetty-jsp + apache-jsp ${cxf.jetty.version} + + + org.mortbay.jasper + apache-jsp + + org.slf4j @@ -159,6 +165,11 @@ tomcat-embed-logging-juli ${cxf.tomcat.version} + + org.apache.tomcat + tomcat-jasper + ${cxf.tomcat.version} + com.ning async-http-client diff --git a/systests/jaxrs/pom.xml b/systests/jaxrs/pom.xml index 544fdbef1d2..826500cbed0 100644 --- a/systests/jaxrs/pom.xml +++ b/systests/jaxrs/pom.xml @@ -169,7 +169,7 @@ org.eclipse.jetty - jetty-jsp + apache-jsp ${cxf.jetty.version} diff --git a/systests/jibx/jaxrs-jibx/pom.xml b/systests/jibx/jaxrs-jibx/pom.xml index 6a8e5855926..b9eed543383 100644 --- a/systests/jibx/jaxrs-jibx/pom.xml +++ b/systests/jibx/jaxrs-jibx/pom.xml @@ -58,7 +58,7 @@ org.eclipse.jetty - jetty-jsp + apache-jsp ${cxf.jetty.version} diff --git a/systests/rs-http-sci/pom.xml b/systests/rs-http-sci/pom.xml index 01e8f5cee6d..c671104ebac 100644 --- a/systests/rs-http-sci/pom.xml +++ b/systests/rs-http-sci/pom.xml @@ -55,7 +55,7 @@ org.eclipse.jetty - jetty-jsp + apache-jsp ${cxf.jetty.version} diff --git a/systests/tracing/pom.xml b/systests/tracing/pom.xml index 8d7793d4716..2f2031ef96e 100644 --- a/systests/tracing/pom.xml +++ b/systests/tracing/pom.xml @@ -45,7 +45,7 @@ org.eclipse.jetty - jetty-jsp + apache-jsp ${cxf.jetty.version} From 033815a0e180b8e1abfcc0c1acdbeb0219ad256d Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 10:48:25 -0500 Subject: [PATCH 0058/1346] Need to get field initialized on Jetty 9.3 --- .../websocket/jetty9/Jetty9WebSocketDestination.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/jetty9/Jetty9WebSocketDestination.java b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/jetty9/Jetty9WebSocketDestination.java index 3f51712affb..bb94430d115 100644 --- a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/jetty9/Jetty9WebSocketDestination.java +++ b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/jetty9/Jetty9WebSocketDestination.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.security.Principal; import java.util.Enumeration; @@ -82,10 +83,19 @@ public Jetty9WebSocketDestination(Bus bus, DestinationRegistry registry, Endpoin webSocketFactory = (WebSocketServletFactory)ClassLoaderUtils .loadClass("org.eclipse.jetty.websocket.server.WebSocketServerFactory", WebSocketServletFactory.class).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { throw new RuntimeException(e); } webSocketFactory.setCreator(new Creator()); + + try { + Field f = webSocketFactory.getClass().getDeclaredField("objectFactory"); + f.setAccessible(true); + f.set(webSocketFactory, f.getType().newInstance()); + } catch (Throwable t) { + //ignore, on Jetty <=9.2 this field doesn't exist + } executor = bus.getExtension(WorkQueueManager.class).getAutomaticWorkQueue(); } From 2ec875978a50cd34d07bd7d3bbfc8583fd30250b Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 10:48:57 -0500 Subject: [PATCH 0059/1346] Flush/close during test in case the stream is buffering --- .../apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java index bc38f430d05..39e64fc165b 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java @@ -96,7 +96,9 @@ public void run() { for (int r = 2, i = 1; i <= 5; r *= 2, i++) { Thread.sleep(500); out.write(Integer.toString(r).getBytes()); + out.flush(); } + out.close(); } catch (Exception e) { e.printStackTrace(); } From 6c67f878e2c210e7f03c4aea52b2ba2eec7d611a Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Wed, 11 Nov 2015 13:57:08 -0500 Subject: [PATCH 0060/1346] More updates to start getting the atmosphere based websocket stuff to work with Jetty 9.3 --- .../DefaultProtocolInterceptor.java | 109 +++++++++++------- .../jaxrs/websocket/BookStoreWebSocket.java | 2 + 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java index b70a4672bf1..54431ced43a 100644 --- a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java +++ b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java @@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.Deque; import java.util.HashMap; import java.util.Map; @@ -32,6 +33,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.transport.websocket.InvalidPathException; import org.apache.cxf.transport.websocket.WebSocketConstants; import org.apache.cxf.transport.websocket.WebSocketUtils; @@ -107,7 +109,7 @@ public Action inspect(final AtmosphereResource r) { AtmosphereRequest request = r.getRequest(); if (request.getAttribute(REQUEST_DISPATCHED) == null) { - AtmosphereResponse response = new WrappedAtmosphereResponse(r.getResponse(), request); + AtmosphereResponse response = null; AtmosphereFramework framework = r.getAtmosphereConfig().framework(); try { @@ -121,6 +123,7 @@ public Action inspect(final AtmosphereResource r) { } try { AtmosphereRequest ar = createAtmosphereRequest(request, data); + response = new WrappedAtmosphereResponse(r.getResponse(), ar); ar.attributes().put(REQUEST_DISPATCHED, "true"); String refid = ar.getHeader(WebSocketConstants.DEFAULT_REQUEST_ID_KEY); if (refid != null) { @@ -138,12 +141,17 @@ public Action inspect(final AtmosphereResource r) { } } catch (Exception e) { LOG.log(Level.WARNING, "Error during request dispatching", e); + if (response == null) { + response = new WrappedAtmosphereResponse(r.getResponse(), request); + } if (e instanceof InvalidPathException) { - response.setStatus(400); + response.setIntHeader(WebSocketUtils.SC_KEY, 400); } else { - response.setStatus(500); + response.setIntHeader(WebSocketUtils.SC_KEY, 500); } - response.getOutputStream().write(createResponse(response, null, true)); + OutputStream out = response.getOutputStream(); + out.write(createResponse(response, null, true)); + out.close(); } return Action.CANCELLED; } catch (IOException e) { @@ -228,7 +236,11 @@ protected byte[] createResponse(AtmosphereResponse response, byte[] payload, boo } if (parent) { // include the status code and content-type and those matched headers - headers.put(WebSocketUtils.SC_KEY, Integer.toString(response.getStatus())); + String sc = response.getHeader(WebSocketUtils.SC_KEY); + if (sc == null) { + sc = Integer.toString(response.getStatus()); + } + headers.put(WebSocketUtils.SC_KEY, sc); if (payload != null && payload.length > 0) { headers.put("Content-Type", response.getContentType()); } @@ -273,47 +285,62 @@ public byte[] error(AtmosphereResponse response, int statusCode, String reasonPh // a workaround to flush the header data upon close when no write operation occurs private class WrappedAtmosphereResponse extends AtmosphereResponse { - WrappedAtmosphereResponse(AtmosphereResponse resp, AtmosphereRequest req) { - super((HttpServletResponse)resp.getResponse(), resp.getAsyncIOWriter(), req, resp.isDestroyable()); + final AtmosphereResponse response; + final ServletOutputStream delegate; + ServletOutputStream sout; + WrappedAtmosphereResponse(AtmosphereResponse resp, AtmosphereRequest req) throws IOException { + super((HttpServletResponse)resp.getResponse(), null, req, resp.isDestroyable()); + response = resp; + response.request(req); + delegate = super.getOutputStream(); } @Override public ServletOutputStream getOutputStream() throws IOException { - final ServletOutputStream delegate = super.getOutputStream(); - return new ServletOutputStream() { - private boolean written; - - @Override - public void write(int i) throws IOException { - written = true; - delegate.write(i); - } - - @Override - public void close() throws IOException { - if (!written) { - delegate.write(createResponse(WrappedAtmosphereResponse.this, null, true)); + if (sout == null) { + sout = new ServletOutputStream() { + CachedOutputStream out = new CachedOutputStream(); + OutputStream getOut() { + if (out == null) { + out = new CachedOutputStream(); + } + return out; + } + void send(boolean complete) throws IOException { + if (out == null) { + return; + } + if (response.getStatus() >= 400) { + int i = response.getStatus(); + response.setStatus(200); + response.addIntHeader(WebSocketUtils.SC_KEY, i); + } + out.flush(); + out.lockOutputStream(); + out.writeCacheTo(delegate); + delegate.flush(); + out.close(); + out = null; } - delegate.close(); - } - - @Override - public void flush() throws IOException { - delegate.flush(); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - written = true; - delegate.write(b, off, len); - } - - @Override - public void write(byte[] b) throws IOException { - written = true; - delegate.write(b); - } - }; + public void write(int i) throws IOException { + getOut().write(i); + } + public void close() throws IOException { + send(true); + delegate.close(); + } + public void flush() throws IOException { + send(false); + } + public void write(byte[] b, int off, int len) throws IOException { + getOut().write(b, off, len); + } + public void write(byte[] b) throws IOException { + getOut().write(b); + } + }; + } + return sout; } } } diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java index 39e64fc165b..888ada03bdf 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/websocket/BookStoreWebSocket.java @@ -121,6 +121,7 @@ public void run() { for (int i = 2; i <= 5; i++) { Thread.sleep(500); out.write(new Book("WebSocket" + i, i)); + out.getEntityStream().flush(); } } catch (Exception e) { e.printStackTrace(); @@ -166,6 +167,7 @@ public String createEvent(@PathParam("name") String name) { OutputStream out = it.next(); try { out.write(("News: event " + name + " created").getBytes()); + out.flush(); } catch (IOException e) { it.remove(); e.printStackTrace(); From d3ea067659eb3f765df0bee6ce7b4abb55f76ab5 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Thu, 12 Nov 2015 10:23:16 -0500 Subject: [PATCH 0061/1346] Recording .gitmergeinfo Changes --- .gitmergeinfo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitmergeinfo b/.gitmergeinfo index b4ea0145480..8bd298fb46a 100644 --- a/.gitmergeinfo +++ b/.gitmergeinfo @@ -1,3 +1,9 @@ origin/master +B 0d4cd0bbcaa6a4f80552d6b38f2a5e721ab20de9 +B 39851b83af116611ce0efe70c4b9a32ee8491523 +B 59b8615053ddcad353fbebcd9a5b1109ae0897a1 B 65e1e07fdb810ec9de135530ca3e3d23821836a3 B 7fc957efa3a193a5f2ae178b8a608717ce4c5b26 +B ced98c6e937bd93f92dac9043fa0406c696bfd84 +B f1b56150d6520e73d2ade2296c3b2f13839e63e5 +B fb30f8bffc85fcc3208fcc0e1eda4b54a89b5d37 From 0f4c8989c6ca32bcc16a34df95088b731e977837 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 12 Nov 2015 21:59:48 +0000 Subject: [PATCH 0062/1346] [CXF-6671] Renaming the method accepting ApplicationInfo to prevent Spring from being confused --- .../java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java | 4 ++-- .../apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java index 1c3564704d3..d4e53ac94ee 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/JAXRSServerFactoryBean.java @@ -97,10 +97,10 @@ public JAXRSServerFactoryBean(JAXRSServiceFactoryBean sf) { * @param app */ public void setApplication(Application app) { - setApplication(new ApplicationInfo(app, getBus())); + setApplicationInfo(new ApplicationInfo(app, getBus())); } - public void setApplication(ApplicationInfo provider) { + public void setApplicationInfo(ApplicationInfo provider) { appProvider = provider; Set appNameBindings = AnnotationUtils.getNameBindings(provider.getProvider() .getClass().getAnnotations()); diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java index 0f7f3e19f30..8332ab8c7be 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/servlet/CXFNonSpringJaxrsServlet.java @@ -510,7 +510,7 @@ protected void createServerFromApplication(String applicationNames, ServletConfi setDocLocation(bean, servletConfig); setSchemasLocations(bean, servletConfig); bean.setBus(getBus()); - bean.setApplication(providerApp); + bean.setApplicationInfo(providerApp); bean.create(); } } From 4ff4cf5f28b22bc85cc107a9d31545c53c04845a Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 13 Nov 2015 10:56:58 +0000 Subject: [PATCH 0063/1346] Makaing sure the code filter can catch all code response errors --- .../rs/security/oauth2/client/AccessDeniedResponse.java | 8 +++++++- .../security/oauth2/client/ClientCodeRequestFilter.java | 4 ++-- .../oauth2/provider/AbstractOAuthJoseJwtProducer.java | 5 +++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java index 9ec28abfde8..16a87bfed80 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/AccessDeniedResponse.java @@ -19,5 +19,11 @@ package org.apache.cxf.rs.security.oauth2.client; public class AccessDeniedResponse { - + private String error; + public AccessDeniedResponse(String error) { + this.error = error; + } + public String getError() { + return error; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 3e312a36ebb..18285a6149f 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -115,10 +115,10 @@ private void checkSecurityContextEnd(ContainerRequestContext rc, if (sc == null || sc.getUserPrincipal() == null) { if (codeParam == null && requestParams.containsKey(OAuthConstants.ERROR_KEY) - && OAuthConstants.ACCESS_DENIED.equals(requestParams.getFirst(OAuthConstants.ERROR_KEY)) && !faultAccessDeniedResponses) { if (!applicationCanHandleAccessDenied) { - rc.abortWith(Response.ok(new AccessDeniedResponse()).build()); + String error = requestParams.getFirst(OAuthConstants.ERROR_KEY); + rc.abortWith(Response.ok(new AccessDeniedResponse(error)).build()); } } else { throw ExceptionUtils.toNotAuthorizedException(null, null); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java index b0a74141296..fec38bca2c0 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java @@ -22,6 +22,7 @@ import javax.crypto.SecretKey; +import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; @@ -44,7 +45,7 @@ protected String processJwt(JwtToken jwt, String clientSecret) { } protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecret) { - if (signWithClientSecret) { + if (signWithClientSecret && !StringUtils.isEmpty(clientSecret)) { Properties props = JwsUtils.loadSignatureOutProperties(false); SignatureAlgorithm sigAlgo = JwsUtils.getSignatureAlgorithm(props, SignatureAlgorithm.HS256); if (AlgorithmUtils.isHmacSign(sigAlgo)) { @@ -54,7 +55,7 @@ protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecr return null; } protected JweEncryptionProvider getInitializedEncryptionProvider(String clientSecret) { - if (encryptWithClientSecret) { + if (encryptWithClientSecret && !StringUtils.isEmpty(clientSecret)) { SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); Properties props = JweUtils.loadEncryptionOutProperties(false); ContentAlgorithm ctAlgo = JweUtils.getContentEncryptionAlgorithm(props, ContentAlgorithm.A128GCM); From e80086821a1f4020247d97bbd62dd8cad81d4ae1 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 13 Nov 2015 11:35:16 +0000 Subject: [PATCH 0064/1346] Making sure an empty/null secret is not used for getting tokens for public clients --- .../cxf/rs/security/oauth2/client/OAuthClientUtils.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java index 971b4811fcf..17471f86741 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java @@ -33,6 +33,7 @@ import javax.ws.rs.core.UriBuilder; import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.rs.security.oauth2.common.AccessTokenGrant; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; @@ -281,7 +282,8 @@ public static ClientAccessToken getAccessToken(WebClient accessTokenService, } } if (consumer != null) { - if (setAuthorizationHeader) { + boolean secretAvailable = !StringUtils.isEmpty(consumer.getSecret()); + if (setAuthorizationHeader && secretAvailable) { StringBuilder sb = new StringBuilder(); sb.append("Basic "); try { @@ -293,7 +295,7 @@ public static ClientAccessToken getAccessToken(WebClient accessTokenService, accessTokenService.replaceHeader("Authorization", sb.toString()); } else { form.param(OAuthConstants.CLIENT_ID, consumer.getKey()); - if (consumer.getSecret() != null) { + if (secretAvailable) { form.param(OAuthConstants.CLIENT_SECRET, consumer.getSecret()); } } @@ -315,7 +317,7 @@ public static ClientAccessToken getAccessToken(WebClient accessTokenService, } else { return token; } - } else if (400 == response.getStatus() && map.containsKey(OAuthConstants.ERROR_KEY)) { + } else if (response.getStatus() >= 400 && map.containsKey(OAuthConstants.ERROR_KEY)) { OAuthError error = new OAuthError(map.get(OAuthConstants.ERROR_KEY), map.get(OAuthConstants.ERROR_DESCRIPTION_KEY)); error.setErrorUri(map.get(OAuthConstants.ERROR_URI_KEY)); From 13521bd10962d41f16f699f42876874b53a448f3 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 13 Nov 2015 11:15:30 +0000 Subject: [PATCH 0065/1346] NPE fix --- .../security/oauth2/services/RedirectionBasedGrantService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index 51ea97ec121..667de929b68 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -271,7 +271,7 @@ protected Response completeAuthorization(MultivaluedMap params) sessionTokenParamName = OAuthConstants.SESSION_AUTHENTICITY_TOKEN; } String sessionToken = params.getFirst(sessionTokenParamName); - if (!compareRequestAndSessionTokens(sessionToken, params, userSubject)) { + if (sessionToken == null || !compareRequestAndSessionTokens(sessionToken, params, userSubject)) { throw ExceptionUtils.toBadRequestException(null, null); } From 2e88028254a89e6ecea2bf607b50f60d66aaeaae Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 13 Nov 2015 12:25:57 +0000 Subject: [PATCH 0066/1346] Use client id instead of principal name if it's available --- .../cxf/rs/security/oauth2/services/AbstractTokenService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java index 29eadcbbcca..61e3165ef29 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java @@ -65,12 +65,12 @@ protected Client authenticateClientIfNeeded(MultivaluedMap param client = getAndValidateClientFromIdAndSecret(clientId, params.getFirst(OAuthConstants.CLIENT_SECRET)); } - } else if (principal.getName() != null) { - client = getClient(principal.getName()); } else { String clientId = retrieveClientId(params); if (clientId != null) { client = getClient(clientId); + } else if (principal.getName() != null) { + client = getClient(principal.getName()); } } if (client == null) { From 22d0c244d3b60aafbd3c070e9599012019de486c Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 13 Nov 2015 15:06:55 +0000 Subject: [PATCH 0067/1346] Fixing some failing tests --- .../apache/cxf/sts/token/provider/JWTTokenProviderTest.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java index 51ef2102909..c81f746fc8d 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java @@ -387,11 +387,7 @@ private Properties getEncryptionProperties() { "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" ); properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); - if (unrestrictedPoliciesInstalled) { - properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); - } else { - properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "restricted/stsstore.jks"); - } + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); return properties; } From 5e8334b2d62fa5ae453ba12becffc7db154d71cb Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 13 Nov 2015 16:28:07 +0000 Subject: [PATCH 0068/1346] Adding JWTValidator --- .../apache/cxf/sts/request/ReceivedToken.java | 7 +- .../validator/jwt/JWTTokenValidator.java | 207 +++++++++++++++ .../validator/JWTTokenValidatorTest.java | 246 ++++++++++++++++++ 3 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java index c2e1aee8127..252ec608e69 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java @@ -33,7 +33,7 @@ /** * This class contains values that have been extracted from a received Token. The Token can be a - * JAXB UsernameTokenType/BinarySecurityTokenType or a DOM Element. + * JAXB UsernameTokenType/BinarySecurityTokenType, a DOM Element or a String. */ public class ReceivedToken { @@ -74,6 +74,11 @@ public ReceivedToken(Object receivedToken) throws STSException { } this.token = receivedToken; isDOMElement = true; + } else if (receivedToken instanceof String) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Found ValidateTarget String"); + } + this.token = receivedToken; } else { LOG.fine("Found ValidateTarget object of unknown type"); throw new STSException( diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java new file mode 100644 index 00000000000..837c3c14f81 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java @@ -0,0 +1,207 @@ +/** + * 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.sts.token.validator.jwt; + +import java.security.KeyStore; +import java.security.Principal; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.security.SimplePrincipal; +import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtUtils; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.request.ReceivedToken; +import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.token.validator.TokenValidator; +import org.apache.cxf.sts.token.validator.TokenValidatorParameters; +import org.apache.cxf.sts.token.validator.TokenValidatorResponse; +import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.Merlin; + +/** + * Validate a SAML Assertion. It is valid if it was issued and signed by this STS. + */ +public class JWTTokenValidator implements TokenValidator { + + private static final Logger LOG = LogUtils.getL7dLogger(JWTTokenValidator.class); + private int clockOffset; + private int ttl; + + /** + * Return true if this TokenValidator implementation is capable of validating the + * ReceivedToken argument. + */ + public boolean canHandleToken(ReceivedToken validateTarget) { + return canHandleToken(validateTarget, null); + } + + /** + * Return true if this TokenValidator implementation is capable of validating the + * ReceivedToken argument. The realm is ignored in this Validator. + */ + public boolean canHandleToken(ReceivedToken validateTarget, String realm) { + Object token = validateTarget.getToken(); + if (token instanceof String) { + try { + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer((String)token); + if (jwtConsumer.getJwtToken() != null) { + return true; + } + } catch (RuntimeException ex) { + return false; + } + } + return false; + } + + /** + * Validate a Token using the given TokenValidatorParameters. + */ + public TokenValidatorResponse validateToken(TokenValidatorParameters tokenParameters) { + LOG.fine("Validating JWT Token"); + STSPropertiesMBean stsProperties = tokenParameters.getStsProperties(); + + TokenValidatorResponse response = new TokenValidatorResponse(); + ReceivedToken validateTarget = tokenParameters.getToken(); + validateTarget.setState(STATE.INVALID); + response.setToken(validateTarget); + + String token = (String)validateTarget.getToken(); + if (token == null) { + return response; + } + + if (token.split("\\.").length != 3) { + LOG.log(Level.WARNING, "JWT Token appears not to be signed. Validation has failed"); + return response; + } + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); + JwtToken jwt = jwtConsumer.getJwtToken(); + + // Verify the signature + Properties verificationProperties = new Properties(); + + Crypto signatureCrypto = stsProperties.getSignatureCrypto(); + String alias = stsProperties.getSignatureUsername(); + + if (alias != null) { + verificationProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias); + } + + if (!(signatureCrypto instanceof Merlin)) { + throw new STSException("Can't get the keystore", STSException.REQUEST_FAILED); + } + KeyStore keystore = ((Merlin)signatureCrypto).getKeyStore(); + verificationProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); + + JwsSignatureVerifier signatureVerifier = + JwsUtils.loadSignatureVerifier(verificationProperties, jwt.getJwsHeaders()); + + if (!jwtConsumer.verifySignatureWith(signatureVerifier)) { + return response; + } + + try { + validateToken(jwt); + } catch (RuntimeException ex) { + LOG.log(Level.WARNING, "JWT token validation failed", ex); + return response; + } + + /* + // Parse roles from the validated token + if (samlRoleParser != null) { + Set roles = + samlRoleParser.parseRolesFromAssertion(principal, null, assertion); + response.setRoles(roles); + } + + // Get the realm of the SAML token + String tokenRealm = null; + if (samlRealmCodec != null) { + tokenRealm = samlRealmCodec.getRealmFromToken(assertion); + // verify the realm against the cached token + if (secToken != null) { + Map props = secToken.getProperties(); + if (props != null) { + String cachedRealm = (String)props.get(STSConstants.TOKEN_REALM); + if (cachedRealm != null && !tokenRealm.equals(cachedRealm)) { + return response; + } + } + } + } + response.setTokenRealm(tokenRealm); + */ + + if (isVerifiedWithAPublicKey(jwt)) { + Principal principal = new SimplePrincipal(jwt.getClaims().getSubject()); + response.setPrincipal(principal); + } + + validateTarget.setState(STATE.VALID); + LOG.fine("JWT Token successfully validated"); + + return response; + } + + private boolean isVerifiedWithAPublicKey(JwtToken jwt) { + String alg = (String)jwt.getJwsHeader(JoseConstants.HEADER_ALGORITHM); + SignatureAlgorithm sigAlg = SignatureAlgorithm.getAlgorithm(alg); + return SignatureAlgorithm.isPublicKeyAlgorithm(sigAlg); + } + + protected void validateToken(JwtToken jwt) { + // If we have no issued time then we need to have an expiry + boolean expiredRequired = jwt.getClaims().getIssuedAt() == null; + JwtUtils.validateJwtExpiry(jwt.getClaims(), clockOffset, expiredRequired); + + JwtUtils.validateJwtNotBefore(jwt.getClaims(), clockOffset, false); + + // If we have no expiry then we must have an issued at + boolean issuedAtRequired = jwt.getClaims().getExpiryTime() == null; + JwtUtils.validateJwtIssuedAt(jwt.getClaims(), ttl, clockOffset, issuedAtRequired); + } + + public int getClockOffset() { + return clockOffset; + } + + public void setClockOffset(int clockOffset) { + this.clockOffset = clockOffset; + } + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java new file mode 100644 index 00000000000..3e2bc059161 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java @@ -0,0 +1,246 @@ +/** + * 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.sts.token.validator; + +import java.io.IOException; +import java.security.Principal; +import java.util.Properties; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.ReceivedToken; +import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.validator.jwt.JWTTokenValidator; +import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSPasswordCallback; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; + +/** + * Some unit tests for validating JWTTokens. + */ +public class JWTTokenValidatorTest extends org.junit.Assert { + private static TokenStore tokenStore = new DefaultInMemoryTokenStore(); + + @org.junit.Test + public void testCreateAndValidateSignedJWT() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(token); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.VALID); + + Principal principal = validatorResponse.getPrincipal(); + assertTrue(principal != null && principal.getName() != null); + } + + @org.junit.Test + public void testInvalidSignature() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + TokenProviderParameters providerParameters = createProviderParameters(); + Crypto crypto = CryptoFactory.getInstance(getEveCryptoProperties()); + CallbackHandler callbackHandler = new EveCallbackHandler(); + providerParameters.getStsProperties().setSignatureCrypto(crypto); + providerParameters.getStsProperties().setCallbackHandler(callbackHandler); + providerParameters.getStsProperties().setSignatureUsername("eve"); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(token); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.INVALID); + } + + private TokenProviderParameters createProviderParameters() throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(JWTTokenProvider.JWT_TOKEN_TYPE); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setTokenStore(tokenStore); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + + return parameters; + } + + private TokenValidatorParameters createValidatorParameters() throws WSSecurityException { + TokenValidatorParameters parameters = new TokenValidatorParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(STSConstants.STATUS); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + parameters.setTokenStore(tokenStore); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + private Properties getEveCryptoProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "evespass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "eve.jks"); + + return properties; + } + + public class EveCallbackHandler implements CallbackHandler { + + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof WSPasswordCallback) { // CXF + WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; + if ("eve".equals(pc.getIdentifier())) { + pc.setPassword("evekpass"); + break; + } + } + } + } + } +} From 4744117f9228e8f25cc2cba2255f6e6a516e2d2a Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 13 Nov 2015 16:46:39 +0000 Subject: [PATCH 0069/1346] Introducing a dedicated property for checking client secret algorithms --- .../oauth2/provider/AbstractOAuthJoseJwtConsumer.java | 9 +++++++-- .../oauth2/provider/AbstractOAuthJoseJwtProducer.java | 9 +++++++-- .../cxf/rs/security/oauth2/utils/OAuthConstants.java | 5 +++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java index 5d2fa3bf3d2..175346e9fb6 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java @@ -31,6 +31,7 @@ import org.apache.cxf.rs.security.jose.jws.JwsUtils; import org.apache.cxf.rs.security.jose.jwt.AbstractJoseJwtConsumer; import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rt.security.crypto.CryptoUtils; public abstract class AbstractOAuthJoseJwtConsumer extends AbstractJoseJwtConsumer { @@ -47,7 +48,9 @@ protected JwtToken getJwtToken(String wrappedJwtToken, String clientSecret) { protected JwsSignatureVerifier getInitializedSignatureVerifier(String clientSecret) { if (verifyWithClientSecret) { Properties props = JwsUtils.loadSignatureInProperties(false); - SignatureAlgorithm sigAlgo = JwsUtils.getSignatureAlgorithm(props, SignatureAlgorithm.HS256); + SignatureAlgorithm sigAlgo = SignatureAlgorithm.getAlgorithm( + props.getProperty(OAuthConstants.CLIENT_SECRET_SIGNATURE_ALGORITHM)); + sigAlgo = sigAlgo != null ? sigAlgo : SignatureAlgorithm.HS256; if (AlgorithmUtils.isHmacSign(sigAlgo)) { return JwsUtils.getHmacSignatureVerifier(clientSecret, sigAlgo); } @@ -59,7 +62,9 @@ protected JweDecryptionProvider getInitializedDecryptionProvider(String clientSe if (decryptWithClientSecret) { SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); Properties props = JweUtils.loadEncryptionInProperties(false); - ContentAlgorithm ctAlgo = JweUtils.getContentEncryptionAlgorithm(props, ContentAlgorithm.A128GCM); + ContentAlgorithm ctAlgo = ContentAlgorithm.getAlgorithm( + props.getProperty(OAuthConstants.CLIENT_SECRET_ENCRYPTION_ALGORITHM)); + ctAlgo = ctAlgo != null ? ctAlgo : ContentAlgorithm.A128GCM; theDecryptionProvider = JweUtils.getDirectKeyJweDecryption(key, ctAlgo); } return theDecryptionProvider; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java index fec38bca2c0..5e1c87072a1 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java @@ -32,6 +32,7 @@ import org.apache.cxf.rs.security.jose.jws.JwsUtils; import org.apache.cxf.rs.security.jose.jwt.AbstractJoseJwtProducer; import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rt.security.crypto.CryptoUtils; public abstract class AbstractOAuthJoseJwtProducer extends AbstractJoseJwtProducer { @@ -47,7 +48,9 @@ protected String processJwt(JwtToken jwt, String clientSecret) { protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecret) { if (signWithClientSecret && !StringUtils.isEmpty(clientSecret)) { Properties props = JwsUtils.loadSignatureOutProperties(false); - SignatureAlgorithm sigAlgo = JwsUtils.getSignatureAlgorithm(props, SignatureAlgorithm.HS256); + SignatureAlgorithm sigAlgo = SignatureAlgorithm.getAlgorithm( + props.getProperty(OAuthConstants.CLIENT_SECRET_SIGNATURE_ALGORITHM)); + sigAlgo = sigAlgo != null ? sigAlgo : SignatureAlgorithm.HS256; if (AlgorithmUtils.isHmacSign(sigAlgo)) { return JwsUtils.getHmacSignatureProvider(clientSecret, sigAlgo); } @@ -58,7 +61,9 @@ protected JweEncryptionProvider getInitializedEncryptionProvider(String clientSe if (encryptWithClientSecret && !StringUtils.isEmpty(clientSecret)) { SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); Properties props = JweUtils.loadEncryptionOutProperties(false); - ContentAlgorithm ctAlgo = JweUtils.getContentEncryptionAlgorithm(props, ContentAlgorithm.A128GCM); + ContentAlgorithm ctAlgo = ContentAlgorithm.getAlgorithm( + props.getProperty(OAuthConstants.CLIENT_SECRET_ENCRYPTION_ALGORITHM)); + ctAlgo = ctAlgo != null ? ctAlgo : ContentAlgorithm.A128GCM; return JweUtils.getDirectKeyJweEncryption(key, ctAlgo); } return null; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java index dea3e112b0c..e15f85e4b5a 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java @@ -129,7 +129,12 @@ public final class OAuthConstants { // Default refresh token scope value - checked by CXF utility code public static final String REFRESH_TOKEN_SCOPE = "refreshToken"; + // Client Secret (JWS) Signature Algorithm + public static final String CLIENT_SECRET_SIGNATURE_ALGORITHM = "client.secret.signature.algorithm"; + // Client Secret (JWE) Encryption Algorithm + public static final String CLIENT_SECRET_ENCRYPTION_ALGORITHM = "client.secret.encryption.algorithm"; + // Client Secret Encrypting Algorithm private OAuthConstants() { } From efac3c9e56559370e2d8c8733f7886f83662b4e4 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Fri, 13 Nov 2015 12:20:34 -0500 Subject: [PATCH 0070/1346] Fix checkstyle --- .../DefaultProtocolInterceptor.java | 93 +++++++++++-------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java index 54431ced43a..3dde4b534cd 100644 --- a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java +++ b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java @@ -298,49 +298,60 @@ private class WrappedAtmosphereResponse extends AtmosphereResponse { @Override public ServletOutputStream getOutputStream() throws IOException { if (sout == null) { - sout = new ServletOutputStream() { - CachedOutputStream out = new CachedOutputStream(); - OutputStream getOut() { - if (out == null) { - out = new CachedOutputStream(); - } - return out; - } - void send(boolean complete) throws IOException { - if (out == null) { - return; - } - if (response.getStatus() >= 400) { - int i = response.getStatus(); - response.setStatus(200); - response.addIntHeader(WebSocketUtils.SC_KEY, i); - } - out.flush(); - out.lockOutputStream(); - out.writeCacheTo(delegate); - delegate.flush(); - out.close(); - out = null; - } - public void write(int i) throws IOException { - getOut().write(i); - } - public void close() throws IOException { - send(true); - delegate.close(); - } - public void flush() throws IOException { - send(false); - } - public void write(byte[] b, int off, int len) throws IOException { - getOut().write(b, off, len); - } - public void write(byte[] b) throws IOException { - getOut().write(b); - } - }; + sout = new BufferedServletOutputStream(); } return sout; } + + private final class BufferedServletOutputStream extends ServletOutputStream { + CachedOutputStream out = new CachedOutputStream(); + + OutputStream getOut() { + if (out == null) { + out = new CachedOutputStream(); + } + return out; + } + + void send(boolean complete) throws IOException { + if (out == null) { + return; + } + if (response.getStatus() >= 400) { + int i = response.getStatus(); + response.setStatus(200); + response.addIntHeader(WebSocketUtils.SC_KEY, i); + } + out.flush(); + out.lockOutputStream(); + out.writeCacheTo(delegate); + delegate.flush(); + out.close(); + out = null; + } + + public void write(int i) throws IOException { + getOut().write(i); + } + + public void close() throws IOException { + send(true); + delegate.close(); + } + + public void flush() throws IOException { + send(false); + } + + public void write(byte[] b, int off, int len) throws IOException { + getOut().write(b, off, len); + } + + public void write(byte[] b) throws IOException { + getOut().write(b); + } + } + + } } From f831e9f5ee56bff9e4472782300f9558ba234f80 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Fri, 13 Nov 2015 12:42:45 -0500 Subject: [PATCH 0071/1346] Fix pmd error --- .../websocket/atmosphere/DefaultProtocolInterceptor.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java index 3dde4b534cd..1a2cd9af60f 100644 --- a/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java +++ b/rt/transports/websocket/src/main/java/org/apache/cxf/transport/websocket/atmosphere/DefaultProtocolInterceptor.java @@ -286,25 +286,28 @@ public byte[] error(AtmosphereResponse response, int statusCode, String reasonPh // a workaround to flush the header data upon close when no write operation occurs private class WrappedAtmosphereResponse extends AtmosphereResponse { final AtmosphereResponse response; - final ServletOutputStream delegate; ServletOutputStream sout; WrappedAtmosphereResponse(AtmosphereResponse resp, AtmosphereRequest req) throws IOException { super((HttpServletResponse)resp.getResponse(), null, req, resp.isDestroyable()); response = resp; response.request(req); - delegate = super.getOutputStream(); } @Override public ServletOutputStream getOutputStream() throws IOException { if (sout == null) { - sout = new BufferedServletOutputStream(); + sout = new BufferedServletOutputStream(super.getOutputStream()); } return sout; } private final class BufferedServletOutputStream extends ServletOutputStream { + final ServletOutputStream delegate; CachedOutputStream out = new CachedOutputStream(); + + BufferedServletOutputStream(ServletOutputStream d) { + delegate = d; + } OutputStream getOut() { if (out == null) { From dd3c8f9d05b549d7aeb3804476bdc3fb344cf2d8 Mon Sep 17 00:00:00 2001 From: Daniel Kulp Date: Fri, 13 Nov 2015 12:44:44 -0500 Subject: [PATCH 0072/1346] Recording .gitmergeinfo Changes --- .gitmergeinfo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitmergeinfo b/.gitmergeinfo index 8bd298fb46a..f2766bb2a6e 100644 --- a/.gitmergeinfo +++ b/.gitmergeinfo @@ -4,6 +4,8 @@ B 39851b83af116611ce0efe70c4b9a32ee8491523 B 59b8615053ddcad353fbebcd9a5b1109ae0897a1 B 65e1e07fdb810ec9de135530ca3e3d23821836a3 B 7fc957efa3a193a5f2ae178b8a608717ce4c5b26 +B a261507ebd3104b1a00298801ec9815ed1e7a728 B ced98c6e937bd93f92dac9043fa0406c696bfd84 +B f0e08b7bea2660542e18294d490e68c7b14aaa4b B f1b56150d6520e73d2ade2296c3b2f13839e63e5 B fb30f8bffc85fcc3208fcc0e1eda4b54a89b5d37 From 36dc41e1b2bddd02def5dafbafd4a86443ab5e30 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 16 Nov 2015 11:15:06 +0000 Subject: [PATCH 0073/1346] Updating the OAuth2 utility code to load client secret providers --- .../cxf/rs/security/jose/jwe/JweUtils.java | 39 ++++++++++------ .../jose/jwe/JweJsonProducerTest.java | 2 +- .../AbstractOAuthJoseJwtConsumer.java | 40 ++++------------ .../AbstractOAuthJoseJwtProducer.java | 33 +++---------- .../security/oauth2/utils/OAuthConstants.java | 5 +- .../rs/security/oauth2/utils/OAuthUtils.java | 46 +++++++++++++++++++ 6 files changed, 91 insertions(+), 74 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 08e5bf9254f..8168184bf77 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -216,26 +216,30 @@ public static ContentEncryptionProvider getContentEncryptionProvider(JsonWebKey return getContentEncryptionProvider(jwk, null); } public static ContentEncryptionProvider getContentEncryptionProvider(JsonWebKey jwk, - String defaultAlgorithm) { - String ctEncryptionAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm : jwk.getAlgorithm(); - ContentEncryptionProvider contentEncryptionProvider = null; + ContentAlgorithm defaultAlgorithm) { + ContentAlgorithm ctAlgo = jwk.getAlgorithm() == null ? defaultAlgorithm + : getContentAlgo(jwk.getAlgorithm()); KeyType keyType = jwk.getKeyType(); if (KeyType.OCTET == keyType) { - return getContentEncryptionProvider(JwkUtils.toSecretKey(jwk), - getContentAlgo(ctEncryptionAlgo)); + return getContentEncryptionProvider(JwkUtils.toSecretKey(jwk), ctAlgo); + } else { + return null; } - return contentEncryptionProvider; } public static ContentEncryptionProvider getContentEncryptionProvider(SecretKey key, ContentAlgorithm algorithm) { + return getContentEncryptionProvider(key.getEncoded(), algorithm); + } + public static ContentEncryptionProvider getContentEncryptionProvider(byte[] key, + ContentAlgorithm algorithm) { if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) { return new AesGcmContentEncryptionAlgorithm(key, null, algorithm); } return null; } - public static ContentEncryptionProvider getContentEncryptionProvider(String algorithm) { - if (AlgorithmUtils.isAesGcm(algorithm)) { - return new AesGcmContentEncryptionAlgorithm(getContentAlgo(algorithm)); + public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm) { + if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) { + return new AesGcmContentEncryptionAlgorithm(algorithm); } return null; } @@ -264,9 +268,11 @@ public static JweEncryption getDirectKeyJweEncryption(JsonWebKey key) { getContentAlgo(key.getAlgorithm())); } public static JweEncryption getDirectKeyJweEncryption(SecretKey key, ContentAlgorithm algo) { + return getDirectKeyJweEncryption(key.getEncoded(), algo); + } + public static JweEncryption getDirectKeyJweEncryption(byte[] key, ContentAlgorithm algo) { if (AlgorithmUtils.isAesCbcHmac(algo.getJwaName())) { - return new AesCbcHmacJweEncryption(algo, key.getEncoded(), - null, new DirectKeyEncryptionAlgorithm()); + return new AesCbcHmacJweEncryption(algo, key, null, new DirectKeyEncryptionAlgorithm()); } else { return new JweEncryption(new DirectKeyEncryptionAlgorithm(), getContentEncryptionProvider(key, algo)); @@ -276,6 +282,9 @@ public static JweDecryption getDirectKeyJweDecryption(JsonWebKey key) { return getDirectKeyJweDecryption(JwkUtils.toSecretKey(key), getContentAlgo(key.getAlgorithm())); } public static JweDecryption getDirectKeyJweDecryption(SecretKey key, ContentAlgorithm algorithm) { + return getDirectKeyJweDecryption(key.getEncoded(), algorithm); + } + public static JweDecryption getDirectKeyJweDecryption(byte[] key, ContentAlgorithm algorithm) { if (AlgorithmUtils.isAesCbcHmac(algorithm.getJwaName())) { return new AesCbcHmacJweDecryption(new DirectKeyDecryptionAlgorithm(key), algorithm); } else { @@ -318,7 +327,7 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe contentAlgo = getContentEncryptionAlgorithm(m, props, ContentAlgorithm.getAlgorithm(jwk.getAlgorithm()), ContentAlgorithm.A128GCM); - ctEncryptionProvider = getContentEncryptionProvider(jwk, contentAlgo.getJwaName()); + ctEncryptionProvider = getContentEncryptionProvider(jwk, contentAlgo); } else { keyAlgo = getKeyEncryptionAlgorithm(m, props, KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()), @@ -486,9 +495,9 @@ public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionPro } public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider keyEncryptionProvider, JweHeaders headers) { - String contentEncryptionAlgo = headers.getContentEncryptionAlgorithm().getJwaName(); - if (AlgorithmUtils.isAesCbcHmac(contentEncryptionAlgo)) { - return new AesCbcHmacJweEncryption(getContentAlgo(contentEncryptionAlgo), keyEncryptionProvider); + ContentAlgorithm contentEncryptionAlgo = headers.getContentEncryptionAlgorithm(); + if (AlgorithmUtils.isAesCbcHmac(contentEncryptionAlgo.getJwaName())) { + return new AesCbcHmacJweEncryption(contentEncryptionAlgo, keyEncryptionProvider); } else { return new JweEncryption(keyEncryptionProvider, getContentEncryptionProvider(contentEncryptionAlgo)); diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java index 67d710573a6..fb3785d3486 100644 --- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java @@ -282,7 +282,7 @@ public void testMultipleRecipients() { KeyEncryptionProvider keyEncryption1 = JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey1, KeyAlgorithm.A128KW); ContentEncryptionProvider contentEncryption = - JweUtils.getContentEncryptionProvider(AlgorithmUtils.A128GCM_ALGO); + JweUtils.getContentEncryptionProvider(ContentAlgorithm.A128GCM); JweEncryptionProvider jwe1 = new JweEncryption(keyEncryption1, contentEncryption); KeyEncryptionProvider keyEncryption2 = JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey2, KeyAlgorithm.A128KW); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java index 175346e9fb6..4e6e7a78863 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtConsumer.java @@ -18,21 +18,12 @@ */ package org.apache.cxf.rs.security.oauth2.provider; -import java.util.Properties; - -import javax.crypto.SecretKey; - -import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; -import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; -import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; -import org.apache.cxf.rs.security.jose.jwe.JweUtils; import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; -import org.apache.cxf.rs.security.jose.jws.JwsUtils; import org.apache.cxf.rs.security.jose.jwt.AbstractJoseJwtConsumer; import org.apache.cxf.rs.security.jose.jwt.JwtToken; -import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; -import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; public abstract class AbstractOAuthJoseJwtConsumer extends AbstractJoseJwtConsumer { @@ -46,29 +37,18 @@ protected JwtToken getJwtToken(String wrappedJwtToken, String clientSecret) { } protected JwsSignatureVerifier getInitializedSignatureVerifier(String clientSecret) { - if (verifyWithClientSecret) { - Properties props = JwsUtils.loadSignatureInProperties(false); - SignatureAlgorithm sigAlgo = SignatureAlgorithm.getAlgorithm( - props.getProperty(OAuthConstants.CLIENT_SECRET_SIGNATURE_ALGORITHM)); - sigAlgo = sigAlgo != null ? sigAlgo : SignatureAlgorithm.HS256; - if (AlgorithmUtils.isHmacSign(sigAlgo)) { - return JwsUtils.getHmacSignatureVerifier(clientSecret, sigAlgo); - } + if (verifyWithClientSecret && !StringUtils.isEmpty(clientSecret)) { + return OAuthUtils.getClientSecretSignatureVerifier(clientSecret); + } else { + return null; } - return null; } protected JweDecryptionProvider getInitializedDecryptionProvider(String clientSecret) { - JweDecryptionProvider theDecryptionProvider = null; - if (decryptWithClientSecret) { - SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); - Properties props = JweUtils.loadEncryptionInProperties(false); - ContentAlgorithm ctAlgo = ContentAlgorithm.getAlgorithm( - props.getProperty(OAuthConstants.CLIENT_SECRET_ENCRYPTION_ALGORITHM)); - ctAlgo = ctAlgo != null ? ctAlgo : ContentAlgorithm.A128GCM; - theDecryptionProvider = JweUtils.getDirectKeyJweDecryption(key, ctAlgo); + if (decryptWithClientSecret && !StringUtils.isEmpty(clientSecret)) { + return OAuthUtils.getClientSecretDecryptionProvider(clientSecret); + } else { + return null; } - return theDecryptionProvider; - } public void setDecryptWithClientSecret(boolean decryptWithClientSecret) { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java index 5e1c87072a1..4563842fe3f 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthJoseJwtProducer.java @@ -18,22 +18,12 @@ */ package org.apache.cxf.rs.security.oauth2.provider; -import java.util.Properties; - -import javax.crypto.SecretKey; - import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; -import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; -import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; -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.AbstractJoseJwtProducer; import org.apache.cxf.rs.security.jose.jwt.JwtToken; -import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; -import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; public abstract class AbstractOAuthJoseJwtProducer extends AbstractJoseJwtProducer { private boolean encryptWithClientSecret; @@ -47,26 +37,17 @@ protected String processJwt(JwtToken jwt, String clientSecret) { protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecret) { if (signWithClientSecret && !StringUtils.isEmpty(clientSecret)) { - Properties props = JwsUtils.loadSignatureOutProperties(false); - SignatureAlgorithm sigAlgo = SignatureAlgorithm.getAlgorithm( - props.getProperty(OAuthConstants.CLIENT_SECRET_SIGNATURE_ALGORITHM)); - sigAlgo = sigAlgo != null ? sigAlgo : SignatureAlgorithm.HS256; - if (AlgorithmUtils.isHmacSign(sigAlgo)) { - return JwsUtils.getHmacSignatureProvider(clientSecret, sigAlgo); - } + return OAuthUtils.getClientSecretSignatureProvider(clientSecret); + } else { + return null; } - return null; } protected JweEncryptionProvider getInitializedEncryptionProvider(String clientSecret) { if (encryptWithClientSecret && !StringUtils.isEmpty(clientSecret)) { - SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); - Properties props = JweUtils.loadEncryptionOutProperties(false); - ContentAlgorithm ctAlgo = ContentAlgorithm.getAlgorithm( - props.getProperty(OAuthConstants.CLIENT_SECRET_ENCRYPTION_ALGORITHM)); - ctAlgo = ctAlgo != null ? ctAlgo : ContentAlgorithm.A128GCM; - return JweUtils.getDirectKeyJweEncryption(key, ctAlgo); + return OAuthUtils.getClientSecretEncryptionProvider(clientSecret); + } else { + return null; } - return null; } public void setEncryptWithClientSecret(boolean encryptWithClientSecret) { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java index e15f85e4b5a..b8f3687cdc8 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java @@ -131,8 +131,9 @@ public final class OAuthConstants { // Client Secret (JWS) Signature Algorithm public static final String CLIENT_SECRET_SIGNATURE_ALGORITHM = "client.secret.signature.algorithm"; - // Client Secret (JWE) Encryption Algorithm - public static final String CLIENT_SECRET_ENCRYPTION_ALGORITHM = "client.secret.encryption.algorithm"; + // Client Secret (JWE) Content Encryption Algorithm + public static final String CLIENT_SECRET_CONTENT_ENCRYPTION_ALGORITHM = + "client.secret.content.encryption.algorithm"; // Client Secret Encrypting Algorithm private OAuthConstants() { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java index ad190dfb46e..51a67a2e9fd 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Properties; import java.util.Set; import javax.servlet.http.HttpSession; @@ -34,6 +35,15 @@ import org.apache.cxf.jaxrs.model.URITemplate; import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.message.Message; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; +import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +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.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; 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.ClientAccessToken; @@ -253,5 +263,41 @@ public static ClientAccessToken toClientAccessToken(ServerAccessToken serverToke return clientToken; } + public static JwsSignatureProvider getClientSecretSignatureProvider(String clientSecret) { + return JwsUtils.getHmacSignatureProvider(clientSecret, getClientSecretSignatureAlgorithm()); + } + public static JwsSignatureVerifier getClientSecretSignatureVerifier(String clientSecret) { + return JwsUtils.getHmacSignatureVerifier(clientSecret, getClientSecretSignatureAlgorithm()); + } + + public static JweDecryptionProvider getClientSecretDecryptionProvider(String clientSecret) { + byte[] key = StringUtils.toBytesUTF8(clientSecret); + return JweUtils.getDirectKeyJweDecryption(key, getClientSecretContentAlgorithm()); + } + + public static JweEncryptionProvider getClientSecretEncryptionProvider(String clientSecret) { + byte[] key = StringUtils.toBytesUTF8(clientSecret); + return JweUtils.getDirectKeyJweEncryption(key, getClientSecretContentAlgorithm()); + } + private static ContentAlgorithm getClientSecretContentAlgorithm() { + Properties props = JweUtils.loadEncryptionInProperties(false); + ContentAlgorithm ctAlgo = ContentAlgorithm.getAlgorithm( + props.getProperty(OAuthConstants.CLIENT_SECRET_CONTENT_ENCRYPTION_ALGORITHM)); + ctAlgo = ctAlgo != null ? ctAlgo : ContentAlgorithm.A128GCM; + return ctAlgo; + } + + private static SignatureAlgorithm getClientSecretSignatureAlgorithm() { + Properties sigProps = JwsUtils.loadSignatureOutProperties(false); + SignatureAlgorithm sigAlgo = SignatureAlgorithm.getAlgorithm( + sigProps.getProperty(OAuthConstants.CLIENT_SECRET_SIGNATURE_ALGORITHM)); + sigAlgo = sigAlgo != null ? sigAlgo : SignatureAlgorithm.HS256; + if (!AlgorithmUtils.isHmacSign(sigAlgo)) { + // Must be HS-based for the symmetric signature + throw new OAuthServiceException(OAuthConstants.SERVER_ERROR); + } else { + return sigAlgo; + } + } } From 11ea395d0fa226c040f4ca3c4c6114a90ce34e06 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 16 Nov 2015 11:34:52 +0000 Subject: [PATCH 0074/1346] Add support for role extraction with JWT validation --- .../validator/jwt/DefaultJWTRoleParser.java | 89 ++++++++ .../token/validator/jwt/JWTRoleParser.java | 44 ++++ .../validator/jwt/JWTTokenValidator.java | 25 ++- .../validator/JWTTokenValidatorTest.java | 198 ++++++++++++++++++ 4 files changed, 349 insertions(+), 7 deletions(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java new file mode 100644 index 00000000000..64d7cf1f094 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/DefaultJWTRoleParser.java @@ -0,0 +1,89 @@ +/** + * 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.sts.token.validator.jwt; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.cxf.common.security.SimpleGroup; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.token.validator.DefaultSubjectRoleParser; + +/** + * A default implementation to extract roles from a JWT token + */ +public class DefaultJWTRoleParser extends DefaultSubjectRoleParser implements JWTRoleParser { + + private boolean useJaasSubject = true; + private String roleClaim; + + /** + * Return the set of User/Principal roles from the token. + * @param principal the Principal associated with the token + * @param subject the JAAS Subject associated with a successful validation of the token + * @param token The JWTToken + * @return the set of User/Principal roles from the token. + */ + public Set parseRolesFromToken( + Principal principal, Subject subject, JwtToken token + ) { + if (subject != null && useJaasSubject) { + return super.parseRolesFromSubject(principal, subject); + } + + Set roles = null; + if (roleClaim != null && token != null && token.getClaims().containsProperty(roleClaim)) { + roles = new HashSet<>(); + String role = token.getClaims().getStringProperty(roleClaim).trim(); + for (String r : role.split(",")) { + roles.add(new SimpleGroup(r)); + } + } else { + roles = Collections.emptySet(); + } + + return roles; + } + + public boolean isUseJaasSubject() { + return useJaasSubject; + } + + /** + * Whether to get roles from the JAAS Subject (if not null) returned from SAML Assertion + * Validation or not. The default is true. + * @param useJaasSubject whether to get roles from the JAAS Subject or not + */ + public void setUseJaasSubject(boolean useJaasSubject) { + this.useJaasSubject = useJaasSubject; + } + + public String getRoleClaim() { + return roleClaim; + } + + public void setRoleClaim(String roleClaim) { + this.roleClaim = roleClaim; + } + +} \ No newline at end of file diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java new file mode 100644 index 00000000000..c651e2a1bc2 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTRoleParser.java @@ -0,0 +1,44 @@ +/** + * 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.sts.token.validator.jwt; + +import java.security.Principal; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.token.validator.SubjectRoleParser; + +/** + * This interface defines a way to extract roles from a JWT Token + */ +public interface JWTRoleParser extends SubjectRoleParser { + + /** + * Return the set of User/Principal roles from the token. + * @param principal the Principal associated with the token + * @param subject the JAAS Subject associated with a successful validation of the token + * @param token The JWTToken + * @return the set of User/Principal roles from the token. + */ + Set parseRolesFromToken( + Principal principal, Subject subject, JwtToken token + ); +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java index 837c3c14f81..110a611736d 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java @@ -21,6 +21,7 @@ import java.security.KeyStore; import java.security.Principal; import java.util.Properties; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -51,6 +52,7 @@ public class JWTTokenValidator implements TokenValidator { private static final Logger LOG = LogUtils.getL7dLogger(JWTTokenValidator.class); private int clockOffset; private int ttl; + private JWTRoleParser roleParser; /** * Return true if this TokenValidator implementation is capable of validating the @@ -134,14 +136,8 @@ public TokenValidatorResponse validateToken(TokenValidatorParameters tokenParame return response; } + /* - // Parse roles from the validated token - if (samlRoleParser != null) { - Set roles = - samlRoleParser.parseRolesFromAssertion(principal, null, assertion); - response.setRoles(roles); - } - // Get the realm of the SAML token String tokenRealm = null; if (samlRealmCodec != null) { @@ -163,6 +159,13 @@ public TokenValidatorResponse validateToken(TokenValidatorParameters tokenParame if (isVerifiedWithAPublicKey(jwt)) { Principal principal = new SimplePrincipal(jwt.getClaims().getSubject()); response.setPrincipal(principal); + + // Parse roles from the validated token + if (roleParser != null) { + Set roles = + roleParser.parseRolesFromToken(principal, null, jwt); + response.setRoles(roles); + } } validateTarget.setState(STATE.VALID); @@ -204,4 +207,12 @@ public int getTtl() { public void setTtl(int ttl) { this.ttl = ttl; } + + public JWTRoleParser getRoleParser() { + return roleParser; + } + + public void setRoleParser(JWTRoleParser roleParser) { + this.roleParser = roleParser; + } } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java index 3e2bc059161..6882751bbd4 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.security.Principal; import java.util.Properties; +import java.util.Set; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; @@ -29,6 +30,7 @@ import org.apache.cxf.jaxws.context.WebServiceContextImpl; import org.apache.cxf.jaxws.context.WrappedMessageContext; import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; import org.apache.cxf.sts.STSConstants; import org.apache.cxf.sts.StaticSTSProperties; import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; @@ -41,7 +43,11 @@ import org.apache.cxf.sts.token.provider.TokenProvider; import org.apache.cxf.sts.token.provider.TokenProviderParameters; import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.DefaultJWTClaimsProvider; +import org.apache.cxf.sts.token.provider.jwt.JWTClaimsProvider; +import org.apache.cxf.sts.token.provider.jwt.JWTClaimsProviderParameters; import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.validator.jwt.DefaultJWTRoleParser; import org.apache.cxf.sts.token.validator.jwt.JWTTokenValidator; import org.apache.cxf.ws.security.tokenstore.TokenStore; import org.apache.wss4j.common.crypto.Crypto; @@ -136,6 +142,182 @@ public void testInvalidSignature() throws Exception { assertTrue(validatorResponse.getToken().getState() == STATE.INVALID); } + @org.junit.Test + public void testUnsignedToken() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(false); + + TokenProviderParameters providerParameters = createProviderParameters(); + Crypto crypto = CryptoFactory.getInstance(getEveCryptoProperties()); + CallbackHandler callbackHandler = new EveCallbackHandler(); + providerParameters.getStsProperties().setSignatureCrypto(crypto); + providerParameters.getStsProperties().setCallbackHandler(callbackHandler); + providerParameters.getStsProperties().setSignatureUsername("eve"); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 2); + + // Validate the token + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(token); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.INVALID); + } + + @org.junit.Test + public void testInvalidConditionJWT() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + DefaultJWTClaimsProvider jwtClaimsProvider = new DefaultJWTClaimsProvider(); + jwtClaimsProvider.setLifetime(1L); + ((JWTTokenProvider)jwtTokenProvider).setJwtClaimsProvider(jwtClaimsProvider); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + Thread.sleep(1500L); + + // Validate the token + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(token); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.INVALID); + } + + @org.junit.Test + public void testChangedSignature() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + DefaultJWTClaimsProvider jwtClaimsProvider = new DefaultJWTClaimsProvider(); + jwtClaimsProvider.setLifetime(1L); + ((JWTTokenProvider)jwtTokenProvider).setJwtClaimsProvider(jwtClaimsProvider); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + // Change the signature + token += "blah"; + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + Thread.sleep(1500L); + + // Validate the token + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(token); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.INVALID); + } + + @org.junit.Test + public void testJWTWithRoles() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + + JWTClaimsProvider claimsProvider = new RoleJWTClaimsProvider("manager"); + ((JWTTokenProvider)jwtTokenProvider).setJwtClaimsProvider(claimsProvider); + + TokenProviderParameters providerParameters = createProviderParameters(); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + // Set the role + DefaultJWTRoleParser roleParser = new DefaultJWTRoleParser(); + roleParser.setRoleClaim("role"); + ((JWTTokenValidator)jwtTokenValidator).setRoleParser(roleParser); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(token); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.VALID); + + Principal principal = validatorResponse.getPrincipal(); + assertTrue(principal != null && principal.getName() != null); + Set roles = validatorResponse.getRoles(); + assertTrue(roles != null && !roles.isEmpty()); + assertTrue(roles.iterator().next().getName().equals("manager")); + } + private TokenProviderParameters createProviderParameters() throws WSSecurityException { TokenProviderParameters parameters = new TokenProviderParameters(); @@ -243,4 +425,20 @@ public void handle(Callback[] callbacks) throws IOException, } } } + + private static class RoleJWTClaimsProvider extends DefaultJWTClaimsProvider { + + private String role; + + public RoleJWTClaimsProvider(String role) { + this.role = role; + } + + @Override + public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters) { + JwtClaims claims = super.getJwtClaims(jwtClaimsProviderParameters); + claims.setProperty("role", role); + return claims; + } + } } From 3832cdfc47a3580da5125bae0b590d9250d4abed Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 16 Nov 2015 12:02:01 +0000 Subject: [PATCH 0075/1346] Recording .gitmergeinfo Changes --- .gitmergeinfo | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmergeinfo b/.gitmergeinfo index f2766bb2a6e..b590b1720bc 100644 --- a/.gitmergeinfo +++ b/.gitmergeinfo @@ -3,6 +3,7 @@ B 0d4cd0bbcaa6a4f80552d6b38f2a5e721ab20de9 B 39851b83af116611ce0efe70c4b9a32ee8491523 B 59b8615053ddcad353fbebcd9a5b1109ae0897a1 B 65e1e07fdb810ec9de135530ca3e3d23821836a3 +B 7b7629682d15345518e66d46e575bf1ac334cf00 B 7fc957efa3a193a5f2ae178b8a608717ce4c5b26 B a261507ebd3104b1a00298801ec9815ed1e7a728 B ced98c6e937bd93f92dac9043fa0406c696bfd84 From ed0421182de3256653710893a770447cdc21bab0 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 16 Nov 2015 12:41:59 +0000 Subject: [PATCH 0076/1346] Adding a description property to OOBAuthorizationResponse --- .../common/OOBAuthorizationResponse.java | 18 +++++++++--------- .../AuthorizationCodeGrantService.java | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OOBAuthorizationResponse.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OOBAuthorizationResponse.java index e1267a3ef94..bc1a4aa805f 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OOBAuthorizationResponse.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OOBAuthorizationResponse.java @@ -22,6 +22,7 @@ public class OOBAuthorizationResponse { private String authorizationCode; private String clientId; + private String clientDescription; private String userId; private long expiresIn; @@ -49,15 +50,6 @@ public void setUserId(String userId) { this.userId = userId; } - @Deprecated - public long getLifetime() { - return expiresIn; - } - @Deprecated - public void setLifetime(long lifetime) { - this.expiresIn = lifetime; - } - public long getExpiresIn() { return expiresIn; } @@ -65,5 +57,13 @@ public long getExpiresIn() { public void setExpiresIn(long lifetime) { this.expiresIn = lifetime; } + + public String getClientDescription() { + return clientDescription; + } + + public void setClientDescription(String clientDescription) { + this.clientDescription = clientDescription; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java index dbb2663bcc0..b78288040c1 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java @@ -128,6 +128,7 @@ protected Response createGrant(OAuthRedirectionState state, if (state.getRedirectUri() == null) { OOBAuthorizationResponse oobResponse = new OOBAuthorizationResponse(); oobResponse.setClientId(client.getClientId()); + oobResponse.setClientDescription(client.getApplicationDescription()); oobResponse.setAuthorizationCode(grant.getCode()); oobResponse.setUserId(userSubject.getLogin()); oobResponse.setExpiresIn(grant.getExpiresIn()); From d2ed88ccb247d4105de34a5b675c0fc198795209 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 16 Nov 2015 13:40:04 +0000 Subject: [PATCH 0077/1346] Update to the redirect_uri validation code --- .../oauth2/services/RedirectionBasedGrantService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index 667de929b68..8435cdf7f13 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -363,8 +363,8 @@ protected String validateRedirectUri(Client client, String redirectUri) { List uris = client.getRedirectUris(); if (redirectUri != null) { - if (!uris.contains(redirectUri)) { - redirectUri = null; + if (!uris.isEmpty() && !uris.contains(redirectUri)) { + reportInvalidRequestError("Client Redirect Uri is invalid"); } } else if (uris.size() == 1 && useRegisteredRedirectUriIfPossible) { redirectUri = uris.get(0); From 1319cddeeca993b6c6f67a392a4807fc46e2e329 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 16 Nov 2015 13:42:00 +0000 Subject: [PATCH 0078/1346] Minor follow up update --- .../security/oauth2/services/RedirectionBasedGrantService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index 8435cdf7f13..887facb214a 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -363,7 +363,7 @@ protected String validateRedirectUri(Client client, String redirectUri) { List uris = client.getRedirectUris(); if (redirectUri != null) { - if (!uris.isEmpty() && !uris.contains(redirectUri)) { + if (!uris.contains(redirectUri)) { reportInvalidRequestError("Client Redirect Uri is invalid"); } } else if (uris.size() == 1 && useRegisteredRedirectUriIfPossible) { From 80fad36cf09200dec0f037bb88dfaf4868808d7c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 16 Nov 2015 14:18:47 +0000 Subject: [PATCH 0079/1346] Setting a default code lifetime to 10 mins --- .../security/oauth2/grants/code/AbstractCodeDataProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java index 7d3b1a091f6..fff16fa84b8 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java @@ -23,7 +23,7 @@ public abstract class AbstractCodeDataProvider extends AbstractOAuthDataProvider implements AuthorizationCodeDataProvider { - private long codeLifetime = 3600L; + private long codeLifetime = 10 * 60; protected AbstractCodeDataProvider() { } From d2b5baa69415ea41f5865a8961c813feead3b22a Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 16 Nov 2015 16:15:21 +0000 Subject: [PATCH 0080/1346] [CXF-6675] - Update commons-collections from 3.2.1 to 3.2.2 --- parent/pom.xml | 1 + tools/common/pom.xml | 11 +++++++++++ tools/validator/pom.xml | 4 ---- tools/wsdlto/core/pom.xml | 4 ---- tools/wsdlto/misc/pom.xml | 4 ---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index 8244caff7b6..6b3c040916b 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -76,6 +76,7 @@ 2.2_2 [2.1.3,3.0.0) 1.10 + 3.2.2 2.6 3.4 10.2.2.0 diff --git a/tools/common/pom.xml b/tools/common/pom.xml index c61e65f626f..d2df1af3e86 100644 --- a/tools/common/pom.xml +++ b/tools/common/pom.xml @@ -45,6 +45,17 @@ org.apache.velocity velocity + + + commons-collections + commons-collections + + + + + commons-collections + commons-collections + ${cxf.commons-collections.version} wsdl4j diff --git a/tools/validator/pom.xml b/tools/validator/pom.xml index bcc05fbdafa..2f761a32af6 100644 --- a/tools/validator/pom.xml +++ b/tools/validator/pom.xml @@ -52,10 +52,6 @@ ${cxf.asm.artifactId} test - - org.apache.velocity - velocity - wsdl4j wsdl4j diff --git a/tools/wsdlto/core/pom.xml b/tools/wsdlto/core/pom.xml index fab8a91b4cc..d525254de94 100644 --- a/tools/wsdlto/core/pom.xml +++ b/tools/wsdlto/core/pom.xml @@ -61,10 +61,6 @@ ${cxf.asm.artifactId} test - - org.apache.velocity - velocity - wsdl4j wsdl4j diff --git a/tools/wsdlto/misc/pom.xml b/tools/wsdlto/misc/pom.xml index 1de3b1c4141..4f92af11efb 100644 --- a/tools/wsdlto/misc/pom.xml +++ b/tools/wsdlto/misc/pom.xml @@ -41,10 +41,6 @@ ${cxf.asm.artifactId} test - - org.apache.velocity - velocity - wsdl4j wsdl4j From 08752e19dbe55998b1a8fd2c3554135be78541d6 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 16 Nov 2015 17:05:48 +0000 Subject: [PATCH 0081/1346] [CXF-6677] Updating the proxy impl --- .../apache/cxf/jaxrs/client/ClientProxyImpl.java | 16 +++++++++++----- .../org/apache/cxf/systest/jaxrs/BookStore.java | 4 ++++ .../systest/jaxrs/JAXRSClientServerBookTest.java | 15 +++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java index 85e4b95a8b6..d41721f2c70 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java @@ -343,11 +343,17 @@ private MultivaluedMap setRequestHeaders(MultivaluedMap consumeTypes = ori.getConsumeTypes(); + if (!consumeTypes.isEmpty() && !consumeTypes.get(0).equals(MediaType.WILDCARD_TYPE)) { + ctType = JAXRSUtils.mediaTypeToString(ori.getConsumeTypes().get(0)); + } else if (bodyClass != null) { + ctType = MediaType.APPLICATION_XML; + } + if (ctType != null) { + headers.putSingle(HttpHeaders.CONTENT_TYPE, ctType); + } } } diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java index cde5c7f91ca..a0d506bc268 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java @@ -425,7 +425,11 @@ public void emptypost() { @PUT @Path("emptyput") + @Consumes("application/json") public void emptyput() { + if (!httpHeaders.getMediaType().toString().equals("application/json")) { + throw new RuntimeException(); + } } @POST diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java index 429cf0e0ece..94c2897aa22 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java @@ -1323,7 +1323,7 @@ public void testEmptyPut() throws Exception { WebClient wc = WebClient.create("http://localhost:" + PORT + "/bookstore/emptyput"); - Response response = wc.put(null); + Response response = wc.type("application/json").put(null); assertEquals(204, response.getStatus()); assertNull(response.getMetadata().getFirst("Content-Type")); @@ -1332,13 +1332,16 @@ public void testEmptyPut() throws Exception { assertNull(response.getMetadata().getFirst("Content-Type")); } + @Test + public void testEmptyPutProxy() throws Exception { + BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class); + store.emptyput(); + assertEquals(204, WebClient.client(store).getResponse().getStatus()); + } + @Test public void testEmptyPostProxy() throws Exception { - String address = "http://localhost:" + PORT; - JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); - bean.setAddress(address); - bean.setResourceClass(BookStore.class); - BookStore store = bean.create(BookStore.class); + BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class); store.emptypost(); assertEquals(204, WebClient.client(store).getResponse().getStatus()); } From 68bdc0ce14b116b961d752493621d8880106d979 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 17 Nov 2015 12:26:39 +0000 Subject: [PATCH 0082/1346] Updating CodeVerifierTransformer --- .../client/ClientCodeRequestFilter.java | 25 ++++++++++++-- .../grants/code/CodeVerifierTransformer.java | 1 + .../grants/code/DigestCodeVerifier.java | 5 +++ .../oauth2/grants/code/PlainCodeVerifier.java | 34 +++++++++++++++++++ .../security/oauth2/utils/OAuthConstants.java | 3 +- 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/PlainCodeVerifier.java diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 18285a6149f..6dfaafeac32 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -36,6 +36,7 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; +import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.ext.MessageContext; import org.apache.cxf.jaxrs.impl.MetadataMap; @@ -45,8 +46,10 @@ import org.apache.cxf.rs.security.oauth2.common.AccessTokenGrant; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant; +import org.apache.cxf.rs.security.oauth2.grants.code.CodeVerifierTransformer; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; +import org.apache.cxf.rt.security.crypto.CryptoUtils; @PreMatching @Priority(Priorities.AUTHENTICATION + 1) @@ -68,6 +71,7 @@ public class ClientCodeRequestFilter implements ContainerRequestFilter { private boolean setFormPostResponseMode; private boolean faultAccessDeniedResponses; private boolean applicationCanHandleAccessDenied; + private CodeVerifierTransformer codeVerifierTransformer; @Override public void filter(ContainerRequestContext rc) throws IOException { @@ -136,19 +140,32 @@ private Response createCodeResponse(ContainerRequestContext rc, UriInfo ui) { getAbsoluteRedirectUri(ui).toString(), theState, theScope); + setFormPostResponseMode(ub, redirectState); setAdditionalCodeRequestParams(ub, redirectState); URI uri = ub.build(); return Response.seeOther(uri).build(); } - protected void setAdditionalCodeRequestParams(UriBuilder ub, MultivaluedMap redirectState) { + protected void setFormPostResponseMode(UriBuilder ub, MultivaluedMap redirectState) { if (setFormPostResponseMode) { // This property is described in OIDC OAuth 2.0 Form Post Response Mode which is technically // can be used without OIDC hence this is set in this filter as opposed to the OIDC specific one. ub.queryParam("response_mode", "form_post"); } } - + protected void setCodeVerifier(UriBuilder ub, MultivaluedMap redirectState) { + if (codeVerifierTransformer != null) { + String codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); + ub.queryParam(OAuthConstants.AUTHORIZATION_CODE_CHALLENGE, + codeVerifierTransformer.transformCodeVerifier(codeVerifier)); + ub.queryParam(OAuthConstants.AUTHORIZATION_CODE_CHALLENGE_METHOD, + codeVerifierTransformer.getChallengeMethod()); + } + } + protected void setAdditionalCodeRequestParams(UriBuilder ub, MultivaluedMap redirectState) { + } + + private URI getAbsoluteRedirectUri(UriInfo ui) { if (redirectUri != null) { return URI.create(redirectUri); @@ -315,4 +332,8 @@ public void setBlockAccessDeniedResponses(boolean blockAccessDeniedResponses) { public void setApplicationCanHandleAccessDenied(boolean applicationCanHandleAccessDenied) { this.applicationCanHandleAccessDenied = applicationCanHandleAccessDenied; } + + public void setCodeVerifierTransformer(CodeVerifierTransformer codeVerifierTransformer) { + this.codeVerifierTransformer = codeVerifierTransformer; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/CodeVerifierTransformer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/CodeVerifierTransformer.java index 02a5e5184d7..c856b7da2bc 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/CodeVerifierTransformer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/CodeVerifierTransformer.java @@ -20,4 +20,5 @@ public interface CodeVerifierTransformer { String transformCodeVerifier(String codeVerifier); + String getChallengeMethod(); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DigestCodeVerifier.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DigestCodeVerifier.java index 9dc64e879c7..7f4325fc389 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DigestCodeVerifier.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DigestCodeVerifier.java @@ -29,6 +29,11 @@ public String transformCodeVerifier(String codeVerifier) { return Base64UrlUtility.encode(digest); } + @Override + public String getChallengeMethod() { + return "S256"; + } + } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/PlainCodeVerifier.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/PlainCodeVerifier.java new file mode 100644 index 00000000000..95d3baf1489 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/PlainCodeVerifier.java @@ -0,0 +1,34 @@ +/** + * 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.grants.code; + +public class PlainCodeVerifier implements CodeVerifierTransformer { + + public String transformCodeVerifier(String codeVerifier) { + return codeVerifier; + } + + @Override + public String getChallengeMethod() { + return "plain"; + } + + + +} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java index b8f3687cdc8..b835e02f71f 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java @@ -57,9 +57,10 @@ public final class OAuthConstants { public static final String BEARER_TOKEN_TYPE = "bearer"; public static final String HAWK_TOKEN_TYPE = "hawk"; - // http://datatracker.ietf.org/doc/draft-sakimura-oauth-tcse + // https://tools.ietf.org/html/rfc7636 public static final String AUTHORIZATION_CODE_VERIFIER = "code_verifier"; public static final String AUTHORIZATION_CODE_CHALLENGE = "code_challenge"; + public static final String AUTHORIZATION_CODE_CHALLENGE_METHOD = "code_challenge_method"; // CXF-specific public static final String REFRESH_TOKEN_TYPE = "refresh"; From 5c541cd9c16ed2d982085410cb801f2f21dbc82d Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 17 Nov 2015 12:29:20 +0000 Subject: [PATCH 0083/1346] Updating ClientCodeRequestFilter to call setCodeVerifier --- .../cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 6dfaafeac32..98ca20845e2 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -141,6 +141,7 @@ private Response createCodeResponse(ContainerRequestContext rc, UriInfo ui) { theState, theScope); setFormPostResponseMode(ub, redirectState); + setCodeVerifier(ub, redirectState); setAdditionalCodeRequestParams(ub, redirectState); URI uri = ub.build(); return Response.seeOther(uri).build(); From 5e5470e7b994b25a318a5b6bbf4e0de5aee430b4 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 17 Nov 2015 13:05:03 +0000 Subject: [PATCH 0084/1346] More code verifier work --- .../client/ClientCodeRequestFilter.java | 28 +++++++++++-------- .../grants/code/AuthorizationCodeGrant.java | 12 ++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 98ca20845e2..ac09dfcded9 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -43,7 +43,6 @@ import org.apache.cxf.jaxrs.utils.ExceptionUtils; import org.apache.cxf.jaxrs.utils.FormUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; -import org.apache.cxf.rs.security.oauth2.common.AccessTokenGrant; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant; import org.apache.cxf.rs.security.oauth2.grants.code.CodeVerifierTransformer; @@ -156,9 +155,8 @@ protected void setFormPostResponseMode(UriBuilder ub, MultivaluedMap redirectState) { if (codeVerifierTransformer != null) { - String codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); ub.queryParam(OAuthConstants.AUTHORIZATION_CODE_CHALLENGE, - codeVerifierTransformer.transformCodeVerifier(codeVerifier)); + redirectState.getFirst(OAuthConstants.AUTHORIZATION_CODE_VERIFIER)); ub.queryParam(OAuthConstants.AUTHORIZATION_CODE_CHALLENGE_METHOD, codeVerifierTransformer.getChallengeMethod()); } @@ -181,13 +179,19 @@ protected void processCodeResponse(ContainerRequestContext rc, UriInfo ui, MultivaluedMap requestParams) { + MultivaluedMap state = null; + if (clientStateManager != null) { + state = clientStateManager.fromRedirectState(mc, requestParams); + } + String codeParam = requestParams.getFirst(OAuthConstants.AUTHORIZATION_CODE_VALUE); ClientAccessToken at = null; if (codeParam != null) { - AccessTokenGrant grant = new AuthorizationCodeGrant(codeParam, getAbsoluteRedirectUri(ui)); + AuthorizationCodeGrant grant = new AuthorizationCodeGrant(codeParam, getAbsoluteRedirectUri(ui)); + grant.setCodeVerifier(state.getFirst(OAuthConstants.AUTHORIZATION_CODE_VERIFIER)); at = OAuthClientUtils.getAccessToken(accessTokenServiceClient, consumer, grant); } - ClientTokenContext tokenContext = initializeClientTokenContext(rc, at, requestParams); + ClientTokenContext tokenContext = initializeClientTokenContext(rc, at, state); if (at != null && clientTokenContextManager != null) { clientTokenContextManager.setClientTokenContext(mc, tokenContext); } @@ -196,11 +200,7 @@ protected void processCodeResponse(ContainerRequestContext rc, protected ClientTokenContext initializeClientTokenContext(ContainerRequestContext rc, ClientAccessToken at, - MultivaluedMap params) { - MultivaluedMap state = null; - if (clientStateManager != null) { - state = clientStateManager.fromRedirectState(mc, params); - } + MultivaluedMap state) { ClientTokenContext tokenContext = createTokenContext(rc, at, state); ((ClientTokenContextImpl)tokenContext).setToken(at); ((ClientTokenContextImpl)tokenContext).setState(state); @@ -226,7 +226,13 @@ protected MultivaluedMap createRedirectState(ContainerRequestCon toCodeRequestState(rc, ui)); } protected MultivaluedMap toCodeRequestState(ContainerRequestContext rc, UriInfo ui) { - return toRequestState(rc, ui); + MultivaluedMap state = toRequestState(rc, ui); + if (codeVerifierTransformer != null) { + String codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); + state.putSingle(OAuthConstants.AUTHORIZATION_CODE_VERIFIER, + codeVerifierTransformer.transformCodeVerifier(codeVerifier)); + } + return state; } protected MultivaluedMap toRequestState(ContainerRequestContext rc, UriInfo ui) { MultivaluedMap requestState = new MetadataMap(); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrant.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrant.java index d599ba2c3da..80119f160be 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrant.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrant.java @@ -36,6 +36,7 @@ public class AuthorizationCodeGrant implements AccessTokenGrant { private static final long serialVersionUID = -3738825769770411453L; private String code; private String redirectUri; + private String codeVerifier; public AuthorizationCodeGrant() { @@ -96,7 +97,18 @@ public MultivaluedMap toMap() { if (redirectUri != null) { map.putSingle(OAuthConstants.REDIRECT_URI, redirectUri); } + if (codeVerifier != null) { + map.putSingle(OAuthConstants.AUTHORIZATION_CODE_VERIFIER, codeVerifier); + } return map; } + public String getCodeVerifier() { + return codeVerifier; + } + + public void setCodeVerifier(String codeVerifier) { + this.codeVerifier = codeVerifier; + } + } From 662b14a6e267808e71e786ff873ee27945122a20 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 17 Nov 2015 12:39:46 +0000 Subject: [PATCH 0085/1346] Added support for realms with JWT validation --- .../ws/security/trust/AbstractSTSClient.java | 4 +- .../sts/operation/TokenIssueOperation.java | 10 +- .../apache/cxf/sts/request/ReceivedToken.java | 5 - .../cxf/sts/token/realm/JWTRealmCodec.java | 36 +++ .../validator/jwt/JWTTokenValidator.java | 56 ++-- .../sts/operation/IssueJWTClaimsUnitTest.java | 61 ++-- .../operation/IssueJWTOnbehalfofUnitTest.java | 46 +-- .../sts/operation/IssueJWTRealmUnitTest.java | 63 ++-- .../cxf/sts/operation/IssueJWTUnitTest.java | 16 +- .../validator/JWTTokenValidatorRealmTest.java | 280 ++++++++++++++++++ .../validator/JWTTokenValidatorTest.java | 23 +- .../cxf/systest/sts/jwt/JWTUnitTest.java | 41 ++- .../cxf/systest/sts/deployment/cxf-sts.xml | 3 + 13 files changed, 514 insertions(+), 130 deletions(-) create mode 100644 services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/JWTRealmCodec.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java index f06ff8072a2..0b6f6195e68 100755 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/AbstractSTSClient.java @@ -1117,7 +1117,9 @@ protected STSResponse validate(SecurityToken tok, String tokentype) writer.writeStartElement("wst", "ValidateTarget", namespace); Element el = tok.getToken(); - StaxUtils.copy(el, writer); + if (el != null) { + StaxUtils.copy(el, writer); + } writer.writeEndElement(); writer.writeEndElement(); diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java index 383535e7339..7a9b57a9686 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenIssueOperation.java @@ -39,7 +39,6 @@ import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.rt.security.claims.ClaimCollection; import org.apache.cxf.sts.QNameConstants; -import org.apache.cxf.sts.STSConstants; import org.apache.cxf.sts.event.STSIssueFailureEvent; import org.apache.cxf.sts.event.STSIssueSuccessEvent; import org.apache.cxf.sts.request.KeyRequirements; @@ -294,14 +293,13 @@ private RequestSecurityTokenResponseType createResponse( } else { if (tokenResponse.getToken() instanceof String) { Document doc = DOMUtils.newDocument(); - Element requestedTokenEl = doc.createElementNS(STSConstants.WST_NS_05_12, - "RequestedSecurityToken"); - requestedTokenEl.setTextContent((String)tokenResponse.getToken()); - response.getAny().add(requestedTokenEl); + Element tokenWrapper = doc.createElementNS(null, "TokenWrapper"); + tokenWrapper.setTextContent((String)tokenResponse.getToken()); + requestedTokenType.setAny(tokenWrapper); } else { requestedTokenType.setAny(tokenResponse.getToken()); - response.getAny().add(requestedToken); } + response.getAny().add(requestedToken); } if (returnReferences) { diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java index 252ec608e69..d2206707c57 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/request/ReceivedToken.java @@ -74,11 +74,6 @@ public ReceivedToken(Object receivedToken) throws STSException { } this.token = receivedToken; isDOMElement = true; - } else if (receivedToken instanceof String) { - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Found ValidateTarget String"); - } - this.token = receivedToken; } else { LOG.fine("Found ValidateTarget object of unknown type"); throw new STSException( diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/JWTRealmCodec.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/JWTRealmCodec.java new file mode 100644 index 00000000000..540e1c86fa6 --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/JWTRealmCodec.java @@ -0,0 +1,36 @@ +/** + * 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.sts.token.realm; + +import org.apache.cxf.rs.security.jose.jwt.JwtToken; + +/** + * This interface defines a pluggable way to return a realm associated with a JWT Token. + */ +public interface JWTRealmCodec { + + /** + * Get the realm associated with the JwtToken parameter + * @param token a JwtToken Object + * @return the realm associated with the JwtToken parameter + */ + String getRealmFromToken(JwtToken token); + +} diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java index 110a611736d..4fb9dec3dbb 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/validator/jwt/JWTTokenValidator.java @@ -25,6 +25,10 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.xml.soap.Node; + +import org.w3c.dom.Element; + import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.security.SimplePrincipal; import org.apache.cxf.rs.security.jose.common.JoseConstants; @@ -37,6 +41,7 @@ import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.request.ReceivedToken; import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.token.realm.JWTRealmCodec; import org.apache.cxf.sts.token.validator.TokenValidator; import org.apache.cxf.sts.token.validator.TokenValidatorParameters; import org.apache.cxf.sts.token.validator.TokenValidatorResponse; @@ -53,6 +58,7 @@ public class JWTTokenValidator implements TokenValidator { private int clockOffset; private int ttl; private JWTRoleParser roleParser; + private JWTRealmCodec realmCodec; /** * Return true if this TokenValidator implementation is capable of validating the @@ -68,14 +74,17 @@ public boolean canHandleToken(ReceivedToken validateTarget) { */ public boolean canHandleToken(ReceivedToken validateTarget, String realm) { Object token = validateTarget.getToken(); - if (token instanceof String) { - try { - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer((String)token); - if (jwtConsumer.getJwtToken() != null) { - return true; + if (token instanceof Element) { + Element tokenEl = (Element)token; + if (tokenEl.getFirstChild().getNodeType() == Node.TEXT_NODE) { + try { + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(tokenEl.getTextContent()); + if (jwtConsumer.getJwtToken() != null) { + return true; + } + } catch (RuntimeException ex) { + return false; } - } catch (RuntimeException ex) { - return false; } } return false; @@ -93,8 +102,8 @@ public TokenValidatorResponse validateToken(TokenValidatorParameters tokenParame validateTarget.setState(STATE.INVALID); response.setToken(validateTarget); - String token = (String)validateTarget.getToken(); - if (token == null) { + String token = ((Element)validateTarget.getToken()).getTextContent(); + if (token == null || "".equals(token)) { return response; } @@ -137,24 +146,11 @@ public TokenValidatorResponse validateToken(TokenValidatorParameters tokenParame } - /* - // Get the realm of the SAML token - String tokenRealm = null; - if (samlRealmCodec != null) { - tokenRealm = samlRealmCodec.getRealmFromToken(assertion); - // verify the realm against the cached token - if (secToken != null) { - Map props = secToken.getProperties(); - if (props != null) { - String cachedRealm = (String)props.get(STSConstants.TOKEN_REALM); - if (cachedRealm != null && !tokenRealm.equals(cachedRealm)) { - return response; - } - } - } + // Get the realm of the JWT Token + if (realmCodec != null) { + String tokenRealm = realmCodec.getRealmFromToken(jwt); + response.setTokenRealm(tokenRealm); } - response.setTokenRealm(tokenRealm); - */ if (isVerifiedWithAPublicKey(jwt)) { Principal principal = new SimplePrincipal(jwt.getClaims().getSubject()); @@ -215,4 +211,12 @@ public JWTRoleParser getRoleParser() { public void setRoleParser(JWTRoleParser roleParser) { this.roleParser = roleParser; } + + public JWTRealmCodec getRealmCodec() { + return realmCodec; + } + + public void setRealmCodec(JWTRealmCodec realmCodec) { + this.realmCodec = realmCodec; + } } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java index ab20e9647d8..82d921bc93d 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTClaimsUnitTest.java @@ -77,6 +77,7 @@ import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.ext.WSSecurityException; @@ -136,20 +137,21 @@ public void testIssueJWTToken() throws Exception { webServiceContext); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); @@ -262,20 +264,21 @@ public void testIssueJaxbJWTToken() throws Exception { webServiceContext); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); assertEquals(jwt.getClaim(ClaimTypes.LASTNAME.toString()), "doe"); @@ -387,20 +390,21 @@ public void testIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateClaims() request, webServiceContext); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponseList.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); // subject unchanged Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); @@ -539,20 +543,21 @@ private void runIssueJWTTokenOnBehalfOfSaml2DifferentRealmFederateIdentity( request, webServiceContext); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponseList.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); // subject changed (to uppercase) Assert.assertEquals("ALICE", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java index dc2352f2210..5d1bfa62029 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTOnbehalfofUnitTest.java @@ -68,6 +68,7 @@ import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.cxf.ws.security.sts.provider.model.secext.AttributedString; import org.apache.cxf.ws.security.sts.provider.model.secext.PasswordString; import org.apache.cxf.ws.security.sts.provider.model.secext.UsernameTokenType; @@ -159,20 +160,21 @@ public void testIssueJWTTokenOnBehalfOfSaml2() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); } @@ -256,20 +258,21 @@ public void testIssueJWTTokenOnBehalfOfUsernameToken() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); } @@ -357,20 +360,21 @@ public void testIssueJWTTokenOnBehalfOfSaml2DifferentRealm() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("ALICE", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java index 3a8b4964d51..46083960a00 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTRealmUnitTest.java @@ -54,6 +54,7 @@ import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.principal.CustomTokenPrincipal; @@ -130,18 +131,19 @@ public void testIssueJWTTokenRealmA() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + assertNotNull(token); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("A-Issuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); } @@ -204,18 +206,19 @@ public void testIssueJWTTokenRealmB() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + assertNotNull(token); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("B-Issuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); } @@ -278,18 +281,19 @@ public void testIssueJWTTokenDefaultRealm() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + assertNotNull(token); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("STS", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); } @@ -371,19 +375,20 @@ public void testIssueJWTTokenRealmBCustomCrypto() throws Exception { response.getRequestSecurityTokenResponse(); assertTrue(!securityTokenResponse.isEmpty()); - // Test the generated token. - String jwtToken = null; + // Test the generated token. + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + assertNotNull(token); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("B-Issuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java index 58d6b25114a..5cd7a3c6710 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueJWTUnitTest.java @@ -51,6 +51,7 @@ import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.cxf.ws.security.tokenstore.TokenStore; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; @@ -127,20 +128,21 @@ public void testIssueJWTToken() throws Exception { assertTrue(!securityTokenResponse.isEmpty()); // Test the generated token. - String jwtToken = null; + Element token = null; for (Object tokenObject : securityTokenResponse.get(0).getAny()) { - if (tokenObject instanceof Element - && REQUESTED_SECURITY_TOKEN.getLocalPart().equals(((Element)tokenObject).getLocalName()) - && REQUESTED_SECURITY_TOKEN.getNamespaceURI().equals(((Element)tokenObject).getNamespaceURI())) { - jwtToken = ((Element)tokenObject).getTextContent(); + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); break; } } - assertNotNull(jwtToken); + assertNotNull(token); // Validate the token - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(jwtToken); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); } diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java new file mode 100644 index 00000000000..a73c3e12d08 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java @@ -0,0 +1,280 @@ +/** + * 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.sts.token.validator; + +import java.security.Principal; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.ReceivedToken; +import org.apache.cxf.sts.request.ReceivedToken.STATE; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.realm.JWTRealmCodec; +import org.apache.cxf.sts.token.realm.RealmProperties; +import org.apache.cxf.sts.token.validator.jwt.JWTTokenValidator; +import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; + +/** + * Some unit tests for validating JWTTokens in different realms + */ +public class JWTTokenValidatorRealmTest extends org.junit.Assert { + private static TokenStore tokenStore = new DefaultInMemoryTokenStore(); + + @org.junit.Test + public void testRealmA() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + ((JWTTokenProvider)jwtTokenProvider).setRealmMap(getRealms()); + + TokenProviderParameters providerParameters = createProviderParameters(); + providerParameters.setRealm("A"); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token - no realm is returned + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.VALID); + assertNull(validatorResponse.getTokenRealm()); + + // Now set the JWTRealmCodec implementation on the Validator + ((JWTTokenValidator)jwtTokenValidator).setRealmCodec(new IssuerJWTRealmCodec()); + + validatorResponse = jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.VALID); + assertTrue(validatorResponse.getTokenRealm().equals("A")); + + Principal principal = validatorResponse.getPrincipal(); + assertTrue(principal != null && principal.getName() != null); + } + + @org.junit.Test + public void testRealmB() throws Exception { + // Create + TokenProvider jwtTokenProvider = new JWTTokenProvider(); + ((JWTTokenProvider)jwtTokenProvider).setSignToken(true); + ((JWTTokenProvider)jwtTokenProvider).setRealmMap(getRealms()); + + TokenProviderParameters providerParameters = createProviderParameters(); + providerParameters.setRealm("B"); + + assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + String token = (String)providerResponse.getToken(); + assertNotNull(token); + assertTrue(token.split("\\.").length == 3); + + // Validate the token - no realm is returned + TokenValidator jwtTokenValidator = new JWTTokenValidator(); + TokenValidatorParameters validatorParameters = createValidatorParameters(); + TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); + + // Create a ValidateTarget consisting of a JWT Token + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); + tokenRequirements.setValidateTarget(validateTarget); + validatorParameters.setToken(validateTarget); + + assertTrue(jwtTokenValidator.canHandleToken(validateTarget)); + + TokenValidatorResponse validatorResponse = + jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.VALID); + assertNull(validatorResponse.getTokenRealm()); + + // Now set the JWTRealmCodec implementation on the Validator + ((JWTTokenValidator)jwtTokenValidator).setRealmCodec(new IssuerJWTRealmCodec()); + + validatorResponse = jwtTokenValidator.validateToken(validatorParameters); + assertTrue(validatorResponse != null); + assertTrue(validatorResponse.getToken() != null); + assertTrue(validatorResponse.getToken().getState() == STATE.VALID); + assertTrue(validatorResponse.getTokenRealm().equals("B")); + + Principal principal = validatorResponse.getPrincipal(); + assertTrue(principal != null && principal.getName() != null); + } + + private Map getRealms() { + // Create Realms + Map realms = new HashMap(); + RealmProperties realm = new RealmProperties(); + realm.setIssuer("A-Issuer"); + realms.put("A", realm); + realm = new RealmProperties(); + realm.setIssuer("B-Issuer"); + realms.put("B", realm); + return realms; + } + + private TokenProviderParameters createProviderParameters() throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(JWTTokenProvider.JWT_TOKEN_TYPE); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setTokenStore(tokenStore); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + + return parameters; + } + + private TokenValidatorParameters createValidatorParameters() throws WSSecurityException { + TokenValidatorParameters parameters = new TokenValidatorParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(STSConstants.STATUS); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + parameters.setTokenStore(tokenStore); + + return parameters; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + private Element createTokenWrapper(String token) { + Document doc = DOMUtils.newDocument(); + Element tokenWrapper = doc.createElementNS(null, "TokenWrapper"); + tokenWrapper.setTextContent(token); + return tokenWrapper; + } + + /** + * This class returns a realm associated with a JWTToken depending on the issuer. + */ + private class IssuerJWTRealmCodec implements JWTRealmCodec { + + public String getRealmFromToken(JwtToken token) { + if ("A-Issuer".equals(token.getClaim(JwtConstants.CLAIM_ISSUER))) { + return "A"; + } else if ("B-Issuer".equals(token.getClaim(JwtConstants.CLAIM_ISSUER))) { + return "B"; + } + return null; + } + + } +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java index 6882751bbd4..13a60b8cab9 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorTest.java @@ -27,6 +27,10 @@ import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.jaxws.context.WebServiceContextImpl; import org.apache.cxf.jaxws.context.WrappedMessageContext; import org.apache.cxf.message.MessageImpl; @@ -85,7 +89,7 @@ public void testCreateAndValidateSignedJWT() throws Exception { TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); // Create a ValidateTarget consisting of a JWT Token - ReceivedToken validateTarget = new ReceivedToken(token); + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); tokenRequirements.setValidateTarget(validateTarget); validatorParameters.setToken(validateTarget); @@ -129,7 +133,7 @@ public void testInvalidSignature() throws Exception { TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); // Create a ValidateTarget consisting of a JWT Token - ReceivedToken validateTarget = new ReceivedToken(token); + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); tokenRequirements.setValidateTarget(validateTarget); validatorParameters.setToken(validateTarget); @@ -170,7 +174,7 @@ public void testUnsignedToken() throws Exception { TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); // Create a ValidateTarget consisting of a JWT Token - ReceivedToken validateTarget = new ReceivedToken(token); + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); tokenRequirements.setValidateTarget(validateTarget); validatorParameters.setToken(validateTarget); @@ -212,7 +216,7 @@ public void testInvalidConditionJWT() throws Exception { TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); // Create a ValidateTarget consisting of a JWT Token - ReceivedToken validateTarget = new ReceivedToken(token); + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); tokenRequirements.setValidateTarget(validateTarget); validatorParameters.setToken(validateTarget); @@ -256,7 +260,7 @@ public void testChangedSignature() throws Exception { TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); // Create a ValidateTarget consisting of a JWT Token - ReceivedToken validateTarget = new ReceivedToken(token); + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); tokenRequirements.setValidateTarget(validateTarget); validatorParameters.setToken(validateTarget); @@ -299,7 +303,7 @@ public void testJWTWithRoles() throws Exception { TokenRequirements tokenRequirements = validatorParameters.getTokenRequirements(); // Create a ValidateTarget consisting of a JWT Token - ReceivedToken validateTarget = new ReceivedToken(token); + ReceivedToken validateTarget = new ReceivedToken(createTokenWrapper(token)); tokenRequirements.setValidateTarget(validateTarget); validatorParameters.setToken(validateTarget); @@ -426,6 +430,13 @@ public void handle(Callback[] callbacks) throws IOException, } } + private Element createTokenWrapper(String token) { + Document doc = DOMUtils.newDocument(); + Element tokenWrapper = doc.createElementNS(null, "TokenWrapper"); + tokenWrapper.setTextContent(token); + return tokenWrapper; + } + private static class RoleJWTClaimsProvider extends DefaultJWTClaimsProvider { private String role; diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java index 837db4317ca..2f094e5e510 100644 --- a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java @@ -67,10 +67,16 @@ public void testIssueJWTToken() throws Exception { SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); + // Issue the token SecurityToken token = requestSecurityToken(JWT_TOKEN_TYPE, bus, DEFAULT_ADDRESS, null, null); assertNotNull(token); - assertNotNull(token.getData()); + assertNotNull(token.getToken()); + + // Validate the token + token = validateSecurityToken(token, bus, null, null); + assertNotNull(token); + assertNotNull(token.getToken()); } private SecurityToken requestSecurityToken( @@ -107,4 +113,37 @@ private SecurityToken requestSecurityToken( return stsClient.requestSecurityToken(endpointAddress); } + + private SecurityToken validateSecurityToken( + SecurityToken token, + Bus bus, + Map msgProperties, + String wsdlPort + ) throws Exception { + STSClient stsClient = new STSClient(bus); + String port = STSPORT; + + stsClient.setWsdlLocation("https://localhost:" + port + "/SecurityTokenService/Transport?wsdl"); + stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); + if (wsdlPort != null) { + stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}" + wsdlPort); + } else { + stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"); + } + + Map properties = msgProperties; + if (properties == null) { + properties = new HashMap(); + properties.put(SecurityConstants.USERNAME, "alice"); + properties.put( + SecurityConstants.CALLBACK_HANDLER, + "org.apache.cxf.systest.sts.common.CommonCallbackHandler" + ); + } + + stsClient.setProperties(properties); + stsClient.setSendKeyType(false); + + return stsClient.validateSecurityToken(token).get(0); + } } diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml index 989873f5d93..f33a137fdd0 100644 --- a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml @@ -57,6 +57,7 @@ + @@ -104,6 +105,8 @@ + + From cec8828bd220abd0d49457df707b9261b50e1a8e Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 17 Nov 2015 13:44:44 +0000 Subject: [PATCH 0086/1346] Fixing checkstyle --- .../java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java index 2f094e5e510..90da0c321e9 100644 --- a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java @@ -136,9 +136,9 @@ private SecurityToken validateSecurityToken( properties = new HashMap(); properties.put(SecurityConstants.USERNAME, "alice"); properties.put( - SecurityConstants.CALLBACK_HANDLER, - "org.apache.cxf.systest.sts.common.CommonCallbackHandler" - ); + SecurityConstants.CALLBACK_HANDLER, + "org.apache.cxf.systest.sts.common.CommonCallbackHandler" + ); } stsClient.setProperties(properties); From 753368bb25f851d858c39b9f2ece764f81e14c2a Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 17 Nov 2015 16:03:15 +0000 Subject: [PATCH 0087/1346] [FEDIZ-134] Making sure a code challenge is available to the code service --- .../client/ClientCodeRequestFilter.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index ac09dfcded9..f712ab67038 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -155,8 +155,9 @@ protected void setFormPostResponseMode(UriBuilder ub, MultivaluedMap redirectState) { if (codeVerifierTransformer != null) { + String codeVerifier = redirectState.getFirst(OAuthConstants.AUTHORIZATION_CODE_VERIFIER); ub.queryParam(OAuthConstants.AUTHORIZATION_CODE_CHALLENGE, - redirectState.getFirst(OAuthConstants.AUTHORIZATION_CODE_VERIFIER)); + codeVerifierTransformer.transformCodeVerifier(codeVerifier)); ub.queryParam(OAuthConstants.AUTHORIZATION_CODE_CHALLENGE_METHOD, codeVerifierTransformer.getChallengeMethod()); } @@ -222,17 +223,22 @@ protected MultivaluedMap createRedirectState(ContainerRequestCon if (clientStateManager == null) { return null; } - return clientStateManager.toRedirectState(mc, - toCodeRequestState(rc, ui)); - } - protected MultivaluedMap toCodeRequestState(ContainerRequestContext rc, UriInfo ui) { - MultivaluedMap state = toRequestState(rc, ui); + String codeVerifier = null; + MultivaluedMap codeRequestState = toCodeRequestState(rc, ui); if (codeVerifierTransformer != null) { - String codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); - state.putSingle(OAuthConstants.AUTHORIZATION_CODE_VERIFIER, - codeVerifierTransformer.transformCodeVerifier(codeVerifier)); + codeVerifier = Base64UrlUtility.encode(CryptoUtils.generateSecureRandomBytes(32)); + codeRequestState.putSingle(OAuthConstants.AUTHORIZATION_CODE_VERIFIER, + codeVerifier); } - return state; + MultivaluedMap redirectState = + clientStateManager.toRedirectState(mc, codeRequestState); + if (redirectState != null) { + redirectState.putSingle(OAuthConstants.AUTHORIZATION_CODE_VERIFIER, codeVerifier); + } + return redirectState; + } + protected MultivaluedMap toCodeRequestState(ContainerRequestContext rc, UriInfo ui) { + return toRequestState(rc, ui); } protected MultivaluedMap toRequestState(ContainerRequestContext rc, UriInfo ui) { MultivaluedMap requestState = new MetadataMap(); From 5bd827c15cf810a43c91f4476ff74e47f51dd049 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 17 Nov 2015 16:11:00 +0000 Subject: [PATCH 0088/1346] Skipping reading the form body if it is empty --- .../src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java index 666fa28bd42..ec5d0e007fc 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/FormUtils.java @@ -126,6 +126,9 @@ public static void populateMapFromString(MultivaluedMap params, String postBody, String enc, boolean decode) { + if (StringUtils.isEmpty(postBody)) { + return; + } List parts = Arrays.asList(StringUtils.split(postBody, "&")); checkNumberOfParts(m, parts.size()); for (String part : parts) { From 610057e6f2bdc7491e2e2266ec7ac41513fcd5f0 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 17 Nov 2015 17:00:11 +0000 Subject: [PATCH 0089/1346] Making sure a device code verifier is linked to the access token via a code grant --- .../common/AccessTokenRegistration.java | 9 +++++ .../oauth2/common/AccessTokenValidation.java | 9 +++++ .../oauth2/common/ServerAccessToken.java | 9 +++++ .../oauth2/grants/AbstractGrantHandler.java | 13 ++++--- .../code/AuthorizationCodeGrantHandler.java | 34 ++++++++++++------- .../provider/AbstractOAuthDataProvider.java | 2 ++ .../utils/crypto/ModelEncryptionSupport.java | 11 ++++-- 7 files changed, 67 insertions(+), 20 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java index 8f844a03627..b2641fc7e2c 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java @@ -31,6 +31,7 @@ public class AccessTokenRegistration { private String grantType; private UserSubject subject; private String audience; + private String clientCodeVerifier; /** * Sets the {@link Client} instance @@ -120,5 +121,13 @@ public String getAudience() { public void setAudience(String audience) { this.audience = audience; } + + public String getClientCodeVerifier() { + return clientCodeVerifier; + } + + public void setClientCodeVerifier(String clientCodeVerifier) { + this.clientCodeVerifier = clientCodeVerifier; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java index 3455a21e070..f25f286f0f2 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java @@ -48,6 +48,7 @@ public class AccessTokenValidation { private UserSubject tokenSubject; private List tokenScopes = new LinkedList(); private String audience; + private String clientCodeVerifier; private Map extraProps = new HashMap(); public AccessTokenValidation() { @@ -68,6 +69,7 @@ public AccessTokenValidation(ServerAccessToken token) { this.tokenSubject = token.getSubject(); this.tokenScopes = token.getScopes(); this.audience = token.getAudience(); + this.clientCodeVerifier = token.getClientCodeVerifier(); } public String getClientId() { @@ -158,5 +160,12 @@ public boolean isClientConfidential() { public void setClientConfidential(boolean isConfidential) { this.isClientConfidential = isConfidential; } + public String getClientCodeVerifier() { + return clientCodeVerifier; + } + + public void setClientCodeVerifier(String clientCodeVerifier) { + this.clientCodeVerifier = clientCodeVerifier; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/ServerAccessToken.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/ServerAccessToken.java index 965b75825eb..d5cc4498b95 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/ServerAccessToken.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/ServerAccessToken.java @@ -36,6 +36,7 @@ public abstract class ServerAccessToken extends AccessToken { private List scopes = new LinkedList(); private UserSubject subject; private String audience; + private String clientCodeVerifier; protected ServerAccessToken() { @@ -149,4 +150,12 @@ protected static ServerAccessToken validateTokenType(ServerAccessToken token, St } return token; } + + public String getClientCodeVerifier() { + return clientCodeVerifier; + } + + public void setClientCodeVerifier(String clientCodeVerifier) { + this.clientCodeVerifier = clientCodeVerifier; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java index dd17dd1c50d..b855af0bd93 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java @@ -84,7 +84,7 @@ private void checkIfGrantSupported(Client client, String requestedGrant) { } } - private String getSingleGrantType() { + protected String getSingleGrantType() { if (supportedGrants.size() > 1) { String errorMessage = "Request grant type must be specified"; LOG.warning(errorMessage); @@ -109,7 +109,7 @@ protected ServerAccessToken doCreateAccessToken(Client client, List requestedScope) { return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope, - null, null); + null, null, null); } protected ServerAccessToken doCreateAccessToken(Client client, @@ -118,14 +118,15 @@ protected ServerAccessToken doCreateAccessToken(Client client, List approvedScope, String audience) { - return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope, approvedScope, audience); + return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope, + approvedScope, audience, null); } protected ServerAccessToken doCreateAccessToken(Client client, UserSubject subject, String requestedGrant, List requestedScope) { - return doCreateAccessToken(client, subject, requestedGrant, requestedScope, null, null); + return doCreateAccessToken(client, subject, requestedGrant, requestedScope, null, null, null); } protected ServerAccessToken doCreateAccessToken(Client client, @@ -133,7 +134,8 @@ protected ServerAccessToken doCreateAccessToken(Client client, String requestedGrant, List requestedScope, List approvedScope, - String audience) { + String audience, + String codeVerifier) { if (!OAuthUtils.validateScopes(requestedScope, client.getRegisteredScopes(), partialMatchScopeValidation)) { throw new OAuthServiceException(new OAuthError(OAuthConstants.INVALID_SCOPE)); @@ -160,6 +162,7 @@ protected ServerAccessToken doCreateAccessToken(Client client, } reg.setApprovedScope(approvedScope); reg.setAudience(audience); + reg.setClientCodeVerifier(codeVerifier); return dataProvider.createAccessToken(reg); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java index 76fcbecd0ed..9a6888a6285 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java @@ -35,6 +35,7 @@ public class AuthorizationCodeGrantHandler extends AbstractGrantHandler { private CodeVerifierTransformer codeVerifierTransformer; + private boolean expectCodeVerifierForPublicClients; public AuthorizationCodeGrantHandler() { super(OAuthConstants.AUTHORIZATION_CODE_GRANT); @@ -71,32 +72,41 @@ public ServerAccessToken createAccessToken(Client client, MultivaluedMap Date: Wed, 18 Nov 2015 11:16:10 +0000 Subject: [PATCH 0090/1346] Adding more STS/JWT validation tests --- .../sts/operation/TokenValidateOperation.java | 13 +- .../ValidateJWTTransformationTest.java | 546 ++++++++++++++++++ .../sts/operation/ValidateJWTUnitTest.java | 226 ++++++++ .../validator/JWTTokenValidatorRealmTest.java | 2 +- 4 files changed, 785 insertions(+), 2 deletions(-) create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTTransformationTest.java create mode 100644 services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTUnitTest.java diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenValidateOperation.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenValidateOperation.java index 74505424818..671094e9828 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenValidateOperation.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/operation/TokenValidateOperation.java @@ -26,7 +26,11 @@ import javax.xml.bind.JAXBElement; import javax.xml.ws.WebServiceContext; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.rt.security.claims.ClaimCollection; import org.apache.cxf.sts.QNameConstants; import org.apache.cxf.sts.RealmParser; @@ -229,7 +233,14 @@ private RequestSecurityTokenResponseType createResponse( QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityTokenType(); JAXBElement requestedToken = QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(requestedTokenType); - requestedTokenType.setAny(tokenProviderResponse.getToken()); + if (tokenProviderResponse.getToken() instanceof String) { + Document doc = DOMUtils.newDocument(); + Element tokenWrapper = doc.createElementNS(null, "TokenWrapper"); + tokenWrapper.setTextContent((String)tokenProviderResponse.getToken()); + requestedTokenType.setAny(tokenWrapper); + } else { + requestedTokenType.setAny(tokenProviderResponse.getToken()); + } response.getAny().add(requestedToken); // Lifetime diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTTransformationTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTTransformationTest.java new file mode 100644 index 00000000000..9caf7b0287e --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTTransformationTest.java @@ -0,0 +1,546 @@ +/** + * 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.sts.operation; + +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 javax.security.auth.callback.CallbackHandler; +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rt.security.claims.Claim; +import org.apache.cxf.rt.security.claims.ClaimCollection; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.claims.ClaimTypes; +import org.apache.cxf.sts.claims.ClaimsAttributeStatementProvider; +import org.apache.cxf.sts.claims.ClaimsHandler; +import org.apache.cxf.sts.claims.ClaimsManager; +import org.apache.cxf.sts.common.CustomClaimsHandler; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.AttributeStatementProvider; +import org.apache.cxf.sts.token.provider.SAMLTokenProvider; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.realm.JWTRealmCodec; +import org.apache.cxf.sts.token.realm.RealmProperties; +import org.apache.cxf.sts.token.validator.SAMLTokenValidator; +import org.apache.cxf.sts.token.validator.TokenValidator; +import org.apache.cxf.sts.token.validator.jwt.JWTTokenValidator; +import org.apache.cxf.ws.security.sts.provider.STSException; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.StatusType; +import org.apache.cxf.ws.security.sts.provider.model.ValidateTargetType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; +import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.apache.wss4j.common.util.DOM2Writer; +import org.apache.wss4j.dom.WSConstants; +import org.junit.Assert; + +/** + * This tests validating a JWT Token + transforming into a SAML token, and vice versa. + */ +public class ValidateJWTTransformationTest extends org.junit.Assert { + + public static final QName REQUESTED_SECURITY_TOKEN = + QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(null).getName(); + private static final QName QNAME_WST_STATUS = + QNameConstants.WS_TRUST_FACTORY.createStatus(null).getName(); + + @org.junit.Test + public void testJWTToSAMLTransformation() throws Exception { + TokenValidateOperation validateOperation = new TokenValidateOperation(); + + // Add Token Validator + List validatorList = new ArrayList(); + validatorList.add(new JWTTokenValidator()); + validateOperation.setTokenValidators(validatorList); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new SAMLTokenProvider()); + validateOperation.setTokenProviders(providerList); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + validateOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, WSConstants.WSS_SAML2_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Create a JWTToken + TokenProviderResponse providerResponse = createJWT(); + Element wrapper = createTokenWrapper((String)providerResponse.getToken()); + Document doc = wrapper.getOwnerDocument(); + wrapper = (Element)doc.appendChild(wrapper); + + ValidateTargetType validateTarget = new ValidateTargetType(); + validateTarget.setAny(wrapper); + + JAXBElement validateTargetType = + new JAXBElement( + QNameConstants.VALIDATE_TARGET, ValidateTargetType.class, validateTarget + ); + request.getAny().add(validateTargetType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Validate a token + RequestSecurityTokenResponseType response = + validateOperation.validate(request, webServiceContext); + assertTrue(validateResponse(response)); + + // Test the generated token. + Element assertion = null; + for (Object tokenObject : response.getAny()) { + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + assertion = (Element)rstType.getAny(); + break; + } + } + + assertNotNull(assertion); + String tokenString = DOM2Writer.nodeToString(assertion); + assertTrue(tokenString.contains("AttributeStatement")); + assertTrue(tokenString.contains("alice")); + assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); + } + + @org.junit.Test + public void testJWTToSAMLTransformationRealm() throws Exception { + TokenValidateOperation validateOperation = new TokenValidateOperation(); + + // Add Token Validator + List validatorList = new ArrayList(); + JWTTokenValidator validator = new JWTTokenValidator(); + validator.setRealmCodec(new CustomJWTRealmCodec()); + validatorList.add(validator); + validateOperation.setTokenValidators(validatorList); + + // Add Token Provider + List providerList = new ArrayList(); + SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); + providerList.add(samlTokenProvider); + validateOperation.setTokenProviders(providerList); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + stsProperties.setRealmParser(new CustomRealmParser()); + stsProperties.setIdentityMapper(new CustomIdentityMapper()); + validateOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, WSConstants.WSS_SAML2_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Create a JWTToken + TokenProviderResponse providerResponse = createJWT(); + Element wrapper = createTokenWrapper((String)providerResponse.getToken()); + Document doc = wrapper.getOwnerDocument(); + wrapper = (Element)doc.appendChild(wrapper); + + ValidateTargetType validateTarget = new ValidateTargetType(); + validateTarget.setAny(wrapper); + + JAXBElement validateTargetType = + new JAXBElement( + QNameConstants.VALIDATE_TARGET, ValidateTargetType.class, validateTarget + ); + request.getAny().add(validateTargetType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + msgCtx.put("url", "https"); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Validate a token - this will fail as the tokenProvider doesn't understand how to handle + // realm "B" + try { + validateOperation.validate(request, webServiceContext); + } catch (STSException ex) { + // expected + } + + samlTokenProvider.setRealmMap(createSamlRealms()); + RequestSecurityTokenResponseType response = validateOperation.validate(request, webServiceContext); + assertTrue(validateResponse(response)); + + // Test the generated token. + Element assertion = null; + for (Object tokenObject : response.getAny()) { + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + assertion = (Element)rstType.getAny(); + break; + } + } + + assertNotNull(assertion); + String tokenString = DOM2Writer.nodeToString(assertion); + assertTrue(tokenString.contains("AttributeStatement")); + assertTrue(tokenString.contains("ALICE")); + assertTrue(tokenString.contains(SAML2Constants.CONF_BEARER)); + } + + @org.junit.Test + public void testSAMLToJWTTransformation() throws Exception { + TokenValidateOperation validateOperation = new TokenValidateOperation(); + + // Add Token Validator + List validatorList = new ArrayList(); + validatorList.add(new SAMLTokenValidator()); + validateOperation.setTokenValidators(validatorList); + + // Add Token Provider + List providerList = new ArrayList(); + providerList.add(new JWTTokenProvider()); + validateOperation.setTokenProviders(providerList); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + validateOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, JWTTokenProvider.JWT_TOKEN_TYPE + ); + request.getAny().add(tokenType); + + // Create a SAML Token + Element samlToken = + createSAMLAssertion(WSConstants.WSS_SAML2_TOKEN_TYPE, crypto, "mystskey", + new PasswordCallbackHandler()); + Document doc = samlToken.getOwnerDocument(); + samlToken = (Element)doc.appendChild(samlToken); + + ValidateTargetType validateTarget = new ValidateTargetType(); + validateTarget.setAny(samlToken); + + JAXBElement validateTargetType = + new JAXBElement( + QNameConstants.VALIDATE_TARGET, ValidateTargetType.class, validateTarget + ); + request.getAny().add(validateTargetType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Validate a token + RequestSecurityTokenResponseType response = + validateOperation.validate(request, webServiceContext); + assertTrue(validateResponse(response)); + + // Test the generated token. + Element token = null; + for (Object tokenObject : response.getAny()) { + if (tokenObject instanceof JAXBElement + && REQUESTED_SECURITY_TOKEN.equals(((JAXBElement)tokenObject).getName())) { + RequestedSecurityTokenType rstType = + (RequestedSecurityTokenType)((JAXBElement)tokenObject).getValue(); + token = (Element)rstType.getAny(); + break; + } + } + + assertNotNull(token); + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token.getTextContent()); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT)); + } + + private Element createTokenWrapper(String token) { + Document doc = DOMUtils.newDocument(); + Element tokenWrapper = doc.createElementNS(null, "TokenWrapper"); + tokenWrapper.setTextContent(token); + return tokenWrapper; + } + + private TokenProviderResponse createJWT() throws WSSecurityException { + TokenProvider tokenProvider = new JWTTokenProvider(); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return providerResponse; + } + + private TokenProviderParameters createProviderParameters(String tokenType) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + + /* + * Create a security context object + */ + private SecurityContext createSecurityContext(final Principal p) { + return new SecurityContext() { + public Principal getUserPrincipal() { + return p; + } + public boolean isUserInRole(String role) { + return false; + } + }; + } + + private Map createSamlRealms() { + // Create Realms + Map samlRealms = new HashMap(); + RealmProperties samlRealm = new RealmProperties(); + samlRealm.setIssuer("A-Issuer"); + samlRealms.put("A", samlRealm); + samlRealm = new RealmProperties(); + samlRealm.setIssuer("B-Issuer"); + samlRealms.put("B", samlRealm); + return samlRealms; + } + + private static class CustomJWTRealmCodec implements JWTRealmCodec { + + public String getRealmFromToken(JwtToken token) { + if ("alice".equals(token.getClaim(JwtConstants.CLAIM_SUBJECT))) { + return "A"; + } + return null; + } + + } + + /** + * Return true if the response has a valid status, false otherwise + */ + private boolean validateResponse(RequestSecurityTokenResponseType response) { + assertTrue(response != null && response.getAny() != null && !response.getAny().isEmpty()); + + for (Object requestObject : response.getAny()) { + if (requestObject instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) requestObject; + if (QNAME_WST_STATUS.equals(jaxbElement.getName())) { + StatusType status = (StatusType)jaxbElement.getValue(); + if (STSConstants.VALID_CODE.equals(status.getCode())) { + return true; + } + } + } + } + return false; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + private Element createSAMLAssertion( + String tokenType, Crypto crypto, String signatureUsername, CallbackHandler callbackHandler + ) throws WSSecurityException { + + SAMLTokenProvider samlTokenProvider = new SAMLTokenProvider(); + List customProviderList = + new ArrayList(); + customProviderList.add(new ClaimsAttributeStatementProvider()); + samlTokenProvider.setAttributeStatementProviders(customProviderList); + + TokenProviderParameters providerParameters = + createProviderParameters( + tokenType, STSConstants.BEARER_KEY_KEYTYPE, crypto, signatureUsername, callbackHandler + ); + + // Set the ClaimsManager + ClaimsManager claimsManager = new ClaimsManager(); + ClaimsHandler claimsHandler = new CustomClaimsHandler(); + claimsManager.setClaimHandlers(Collections.singletonList(claimsHandler)); + providerParameters.setClaimsManager(claimsManager); + + ClaimCollection requestedClaims = new ClaimCollection(); + Claim requestClaim = new Claim(); + requestClaim.setClaimType(ClaimTypes.LASTNAME); + requestClaim.setOptional(false); + requestedClaims.add(requestClaim); + providerParameters.setRequestedSecondaryClaims(requestedClaims); + + TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return (Element)providerResponse.getToken(); + } + + private TokenProviderParameters createProviderParameters( + String tokenType, String keyType, Crypto crypto, + String signatureUsername, CallbackHandler callbackHandler + ) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + keyRequirements.setKeyType(keyType); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername(signatureUsername); + stsProperties.setCallbackHandler(callbackHandler); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTUnitTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTUnitTest.java new file mode 100644 index 00000000000..f44a06d75f1 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/ValidateJWTUnitTest.java @@ -0,0 +1,226 @@ +/** + * 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.sts.operation; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.jaxws.context.WebServiceContextImpl; +import org.apache.cxf.jaxws.context.WrappedMessageContext; +import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.sts.QNameConstants; +import org.apache.cxf.sts.STSConstants; +import org.apache.cxf.sts.STSPropertiesMBean; +import org.apache.cxf.sts.StaticSTSProperties; +import org.apache.cxf.sts.common.PasswordCallbackHandler; +import org.apache.cxf.sts.request.KeyRequirements; +import org.apache.cxf.sts.request.TokenRequirements; +import org.apache.cxf.sts.service.EncryptionProperties; +import org.apache.cxf.sts.token.provider.TokenProvider; +import org.apache.cxf.sts.token.provider.TokenProviderParameters; +import org.apache.cxf.sts.token.provider.TokenProviderResponse; +import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider; +import org.apache.cxf.sts.token.validator.TokenValidator; +import org.apache.cxf.sts.token.validator.jwt.JWTTokenValidator; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; +import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType; +import org.apache.cxf.ws.security.sts.provider.model.StatusType; +import org.apache.cxf.ws.security.sts.provider.model.ValidateTargetType; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoFactory; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.principal.CustomTokenPrincipal; + +/** + * Some unit tests for the validate operation to validate JWT Tokens. + */ +public class ValidateJWTUnitTest extends org.junit.Assert { + + public static final QName REQUESTED_SECURITY_TOKEN = + QNameConstants.WS_TRUST_FACTORY.createRequestedSecurityToken(null).getName(); + private static final QName QNAME_WST_STATUS = + QNameConstants.WS_TRUST_FACTORY.createStatus(null).getName(); + + @org.junit.Test + public void testValidateJWT() throws Exception { + TokenValidateOperation validateOperation = new TokenValidateOperation(); + + // Add Token Validator + List validatorList = new ArrayList(); + validatorList.add(new JWTTokenValidator()); + validateOperation.setTokenValidators(validatorList); + + // Add STSProperties object + STSPropertiesMBean stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setEncryptionCrypto(crypto); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setEncryptionUsername("myservicekey"); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + validateOperation.setStsProperties(stsProperties); + + // Mock up a request + RequestSecurityTokenType request = new RequestSecurityTokenType(); + JAXBElement tokenType = + new JAXBElement( + QNameConstants.TOKEN_TYPE, String.class, STSConstants.STATUS + ); + request.getAny().add(tokenType); + + // Get a JWTToken via the JWTTokenProvider + TokenProviderResponse providerResponse = createJWT(); + Element wrapper = createTokenWrapper((String)providerResponse.getToken()); + Document doc = wrapper.getOwnerDocument(); + wrapper = (Element)doc.appendChild(wrapper); + ValidateTargetType validateTarget = new ValidateTargetType(); + validateTarget.setAny(wrapper); + + JAXBElement validateTargetType = + new JAXBElement( + QNameConstants.VALIDATE_TARGET, ValidateTargetType.class, validateTarget + ); + request.getAny().add(validateTargetType); + + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + msgCtx.put( + SecurityContext.class.getName(), + createSecurityContext(new CustomTokenPrincipal("alice")) + ); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + + // Validate a token + RequestSecurityTokenResponseType response = + validateOperation.validate(request, webServiceContext); + assertTrue(validateResponse(response)); + } + + private Element createTokenWrapper(String token) { + Document doc = DOMUtils.newDocument(); + Element tokenWrapper = doc.createElementNS(null, "TokenWrapper"); + tokenWrapper.setTextContent(token); + return tokenWrapper; + } + + /* + * Create a security context object + */ + private SecurityContext createSecurityContext(final Principal p) { + return new SecurityContext() { + public Principal getUserPrincipal() { + return p; + } + public boolean isUserInRole(String role) { + return false; + } + }; + } + + /** + * Return true if the response has a valid status, false otherwise + */ + private boolean validateResponse(RequestSecurityTokenResponseType response) { + assertTrue(response != null && response.getAny() != null && !response.getAny().isEmpty()); + + for (Object requestObject : response.getAny()) { + if (requestObject instanceof JAXBElement) { + JAXBElement jaxbElement = (JAXBElement) requestObject; + if (QNAME_WST_STATUS.equals(jaxbElement.getName())) { + StatusType status = (StatusType)jaxbElement.getValue(); + if (STSConstants.VALID_CODE.equals(status.getCode())) { + return true; + } + } + } + } + return false; + } + + private Properties getEncryptionProperties() { + Properties properties = new Properties(); + properties.put( + "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin" + ); + properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass"); + properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks"); + + return properties; + } + + private TokenProviderResponse createJWT() throws WSSecurityException { + TokenProvider tokenProvider = new JWTTokenProvider(); + + TokenProviderParameters providerParameters = + createProviderParameters(JWTTokenProvider.JWT_TOKEN_TYPE); + + assertTrue(tokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE)); + TokenProviderResponse providerResponse = tokenProvider.createToken(providerParameters); + assertTrue(providerResponse != null); + assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null); + + return providerResponse; + } + + private TokenProviderParameters createProviderParameters(String tokenType) throws WSSecurityException { + TokenProviderParameters parameters = new TokenProviderParameters(); + + TokenRequirements tokenRequirements = new TokenRequirements(); + tokenRequirements.setTokenType(tokenType); + parameters.setTokenRequirements(tokenRequirements); + + KeyRequirements keyRequirements = new KeyRequirements(); + parameters.setKeyRequirements(keyRequirements); + + parameters.setPrincipal(new CustomTokenPrincipal("alice")); + // Mock up message context + MessageImpl msg = new MessageImpl(); + WrappedMessageContext msgCtx = new WrappedMessageContext(msg); + WebServiceContextImpl webServiceContext = new WebServiceContextImpl(msgCtx); + parameters.setWebServiceContext(webServiceContext); + + parameters.setAppliesToAddress("http://dummy-service.com/dummy"); + + // Add STSProperties object + StaticSTSProperties stsProperties = new StaticSTSProperties(); + Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties()); + stsProperties.setSignatureCrypto(crypto); + stsProperties.setSignatureUsername("mystskey"); + stsProperties.setCallbackHandler(new PasswordCallbackHandler()); + stsProperties.setIssuer("STS"); + parameters.setStsProperties(stsProperties); + + parameters.setEncryptionProperties(new EncryptionProperties()); + + return parameters; + } + + +} diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java index a73c3e12d08..98283e94aab 100644 --- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/validator/JWTTokenValidatorRealmTest.java @@ -265,7 +265,7 @@ private Element createTokenWrapper(String token) { /** * This class returns a realm associated with a JWTToken depending on the issuer. */ - private class IssuerJWTRealmCodec implements JWTRealmCodec { + private static class IssuerJWTRealmCodec implements JWTRealmCodec { public String getRealmFromToken(JwtToken token) { if ("A-Issuer".equals(token.getClaim(JwtConstants.CLAIM_ISSUER))) { From 2dd91b898f58e99000711810ac0d179a3614d15d Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 18 Nov 2015 12:56:28 +0000 Subject: [PATCH 0091/1346] Adding OAuthServiceExceptionMapper, can be useful when the error needs to be mapped to a form --- .../provider/OAuthServiceExceptionMapper.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServiceExceptionMapper.java diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServiceExceptionMapper.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServiceExceptionMapper.java new file mode 100644 index 00000000000..75455700943 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServiceExceptionMapper.java @@ -0,0 +1,31 @@ +/** + * 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.provider; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +public class OAuthServiceExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(OAuthServiceException exception) { + return Response.ok().entity(exception.getError()).build(); + } + +} From a786d19fd175345eb039f958aed1c8b17964d12e Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 18 Nov 2015 14:48:44 +0000 Subject: [PATCH 0092/1346] [CXF-6682] Making sure UirBuilder is always initialized --- .../main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java index d35a2edf554..2ceff49364e 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java @@ -37,7 +37,7 @@ public class LinkBuilderImpl implements Builder { private static final String DOUBLE_QUOTE = "\""; - private UriBuilder ub; + private UriBuilder ub = new UriBuilderImpl(); private URI baseUri; private Map params = new HashMap(6); From 2920c9bdb058cc3b98603bd6cf50df347f1ae427 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 18 Nov 2015 15:32:54 +0000 Subject: [PATCH 0093/1346] [CXF-6629] Switching to ConcurrentHashMap --- .../org/apache/cxf/jaxrs/model/AbstractResourceInfo.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java index d73fe53475b..1e6dd7edc3b 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/AbstractResourceInfo.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import javax.ws.rs.core.Application; @@ -208,7 +209,7 @@ private Map, Map>> getProxyMap(String prop, property = bus.getProperty(prop); if (property == null && create) { Map, Map>> map - = Collections.synchronizedMap(new HashMap, Map>>(2)); + = new ConcurrentHashMap, Map>>(2); bus.setProperty(prop, map); property = map; } @@ -229,7 +230,7 @@ private Map, Map, ThreadLocalProxy>> getConstructorProxyMap Object property = bus.getProperty(CONSTRUCTOR_PROXY_MAP); if (property == null) { Map, Map, ThreadLocalProxy>> map - = Collections.synchronizedMap(new HashMap, Map, ThreadLocalProxy>>(2)); + = new ConcurrentHashMap, Map, ThreadLocalProxy>>(2); bus.setProperty(CONSTRUCTOR_PROXY_MAP, map); property = map; } @@ -379,7 +380,7 @@ private void addToMap(Map, Map> proxyMap, V proxy) { Map proxies = proxyMap.get(serviceClass); if (proxies == null) { - proxies = Collections.synchronizedMap(new HashMap()); + proxies = new ConcurrentHashMap(); proxyMap.put(serviceClass, proxies); } if (!proxies.containsKey(f)) { From 577eb154c09a0654fc1d54d4743b369b84d3fd5a Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 18 Nov 2015 16:06:10 +0000 Subject: [PATCH 0094/1346] [CXF-6682] Initializing UB later --- .../org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java | 11 +++++++++-- .../apache/cxf/jaxrs/impl/LinkBuilderImplTest.java | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java index 2ceff49364e..389cc1cb18c 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/LinkBuilderImpl.java @@ -37,7 +37,7 @@ public class LinkBuilderImpl implements Builder { private static final String DOUBLE_QUOTE = "\""; - private UriBuilder ub = new UriBuilderImpl(); + private UriBuilder ub; private URI baseUri; private Map params = new HashMap(6); @@ -55,9 +55,16 @@ public Link buildRelativized(URI requestUri, Object... values) { } private URI getResolvedUri(Object... values) { + if (ub == null) { + ub = new UriBuilderImpl(); + if (baseUri != null) { + ub.uri(baseUri); + } + + } URI uri = ub.build(values); - if (!uri.isAbsolute() && baseUri != null) { + if (!uri.isAbsolute() && baseUri != null && baseUri.isAbsolute()) { UriBuilder linkUriBuilder = UriBuilder.fromUri(baseUri); return HttpUtils.resolve(linkUriBuilder, uri); } else { diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java index 70920cc2531..3662eb50054 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java @@ -58,6 +58,12 @@ public String toString() { assertEquals(link.toString(), expected); } + @Test + public void testSelfLink() throws Exception { + Link link = new LinkBuilderImpl().baseUri("http://localhost:8080/resource/1").rel("self").build(); + assertEquals(";rel=\"self\"", link.toString()); + } + @Test public void testBuildManyRels() throws Exception { Link.Builder linkBuilder = new LinkBuilderImpl(); From 29dbad4a995ba09d97b131807585b46cb02c43af Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 19 Nov 2015 13:16:06 +0000 Subject: [PATCH 0095/1346] Making sure an implicit or code nonce is available to OAuthDataProviders --- .../oauth2/common/AccessTokenRegistration.java | 9 +++++++++ .../security/oauth2/common/OAuthRedirectionState.java | 11 +++++++++++ .../grants/code/AuthorizationCodeRegistration.java | 7 +++++++ .../oauth2/provider/JoseSessionTokenProvider.java | 7 +++++++ .../oauth2/services/AbstractImplicitGrantService.java | 1 + .../services/AuthorizationCodeGrantService.java | 1 + .../oauth2/services/RedirectionBasedGrantService.java | 1 + .../cxf/rs/security/oauth2/utils/OAuthConstants.java | 1 + 8 files changed, 38 insertions(+) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java index b2641fc7e2c..db443dab18d 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenRegistration.java @@ -31,6 +31,7 @@ public class AccessTokenRegistration { private String grantType; private UserSubject subject; private String audience; + private String nonce; private String clientCodeVerifier; /** @@ -129,5 +130,13 @@ public String getClientCodeVerifier() { public void setClientCodeVerifier(String clientCodeVerifier) { this.clientCodeVerifier = clientCodeVerifier; } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthRedirectionState.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthRedirectionState.java index 0f05abdc0b6..4acc109e870 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthRedirectionState.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthRedirectionState.java @@ -28,6 +28,7 @@ public class OAuthRedirectionState implements Serializable { private String state; private String proposedScope; private String audience; + private String nonce; private String clientCodeChallenge; public OAuthRedirectionState() { @@ -112,4 +113,14 @@ public String getClientCodeChallenge() { public void setClientCodeChallenge(String clientCodeChallenge) { this.clientCodeChallenge = clientCodeChallenge; } + + + public String getNonce() { + return nonce; + } + + + public void setNonce(String nonce) { + this.nonce = nonce; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeRegistration.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeRegistration.java index a7126b44a47..1319cad7ad0 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeRegistration.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeRegistration.java @@ -35,6 +35,7 @@ public class AuthorizationCodeRegistration { private String redirectUri; private UserSubject subject; private String audience; + private String nonce; private String clientCodeChallenge; /** @@ -126,4 +127,10 @@ public String getClientCodeChallenge() { public void setClientCodeChallenge(String clientCodeChallenge) { this.clientCodeChallenge = clientCodeChallenge; } + public String getNonce() { + return nonce; + } + public void setNonce(String nonce) { + this.nonce = nonce; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java index 0575f063fbd..a6a1c4cfd40 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java @@ -163,6 +163,9 @@ private OAuthRedirectionState convertStateStringToState(String stateString) { if (!StringUtils.isEmpty(parts[5])) { state.setRedirectUri(parts[5]); } + if (!StringUtils.isEmpty(parts[6])) { + state.setRedirectUri(parts[6]); + } return state; } protected String convertStateToString(OAuthRedirectionState secData) { @@ -184,6 +187,10 @@ protected String convertStateToString(OAuthRedirectionState secData) { state.append(ModelEncryptionSupport.SEP); // 5: redirect uri state.append(ModelEncryptionSupport.tokenizeString(secData.getRedirectUri())); + state.append(ModelEncryptionSupport.SEP); + // 6: nonce + state.append(ModelEncryptionSupport.tokenizeString(secData.getNonce())); + return state.toString(); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java index d78feafbf96..139c05bad3d 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java @@ -78,6 +78,7 @@ protected Response createGrant(OAuthRedirectionState state, reg.setApprovedScope(approvedScope); } reg.setAudience(state.getAudience()); + reg.setNonce(state.getNonce()); token = getDataProvider().createAccessToken(reg); } } else { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java index b78288040c1..79559c73132 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationCodeGrantService.java @@ -113,6 +113,7 @@ protected Response createGrant(OAuthRedirectionState state, } codeReg.setSubject(userSubject); codeReg.setAudience(state.getAudience()); + codeReg.setNonce(state.getNonce()); codeReg.setClientCodeChallenge(state.getClientCodeChallenge()); ServerAuthorizationCodeGrant grant = null; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index 887facb214a..4d96f9a9723 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -211,6 +211,7 @@ protected OAuthAuthorizationData createAuthorizationData(Client client, secData.setState(params.getFirst(OAuthConstants.STATE)); secData.setRedirectUri(redirectUri); secData.setAudience(params.getFirst(OAuthConstants.CLIENT_AUDIENCE)); + secData.setNonce(params.getFirst(OAuthConstants.NONCE)); secData.setClientId(client.getClientId()); secData.setProposedScope(params.getFirst(OAuthConstants.SCOPE)); if (!authorizationCanBeSkipped) { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java index b835e02f71f..8a5d457f64b 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java @@ -27,6 +27,7 @@ public final class OAuthConstants { public static final String CLIENT_ID = "client_id"; public static final String CLIENT_SECRET = "client_secret"; public static final String CLIENT_AUDIENCE = "audience"; + public static final String NONCE = "nonce"; public static final String REDIRECT_URI = "redirect_uri"; public static final String SCOPE = "scope"; From 20076a8c3ee335121dcb580640d8d98f5f48a6d7 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 19 Nov 2015 13:42:22 +0000 Subject: [PATCH 0096/1346] One more nonce related update --- .../security/oauth2/services/RedirectionBasedGrantService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index 4d96f9a9723..cc9baba421b 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -248,6 +248,7 @@ protected OAuthRedirectionState recreateRedirectionStateFromSession( state.setAudience(params.getFirst(OAuthConstants.CLIENT_AUDIENCE)); state.setProposedScope(params.getFirst(OAuthConstants.SCOPE)); state.setState(params.getFirst(OAuthConstants.STATE)); + state.setNonce(params.getFirst(OAuthConstants.NONCE)); } return state; } From 4b577dc98cc61e4aead2f0bdff6d017c8a7679df Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 19 Nov 2015 13:48:30 +0000 Subject: [PATCH 0097/1346] Sorry, 1 more nonce update --- .../rs/security/oauth2/provider/JoseSessionTokenProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java index a6a1c4cfd40..fd25fa78840 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/JoseSessionTokenProvider.java @@ -164,7 +164,7 @@ private OAuthRedirectionState convertStateStringToState(String stateString) { state.setRedirectUri(parts[5]); } if (!StringUtils.isEmpty(parts[6])) { - state.setRedirectUri(parts[6]); + state.setNonce(parts[6]); } return state; } From 8c09f501af1d7fe77219396f1e0a5de65146bbe6 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 19 Nov 2015 14:13:01 +0000 Subject: [PATCH 0098/1346] Making it easier to validate the nonce flows with the Memory provider --- .../oauth2/client/JoseClientCodeStateManager.java | 2 +- .../oauth2/client/MemoryClientCodeStateManager.java | 11 ++++++++++- .../apache/cxf/rs/security/oidc/common/IdToken.java | 3 ++- .../security/oidc/rp/OidcClientCodeRequestFilter.java | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java index afc5c9671f0..e269d1952fa 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java @@ -61,7 +61,7 @@ public MultivaluedMap toRedirectState(MessageContext mc, if (generateNonce && theSigProvider != null) { JwsCompactProducer nonceProducer = new JwsCompactProducer(OAuthUtils.generateRandomTokenKey()); String nonceParam = nonceProducer.signWith(theSigProvider); - requestState.putSingle("nonce", nonceParam); + requestState.putSingle(OAuthConstants.NONCE, nonceParam); } Map stateMap = CastUtils.cast((Map)requestState); String json = jsonp.toJson(stateMap); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java index 727839b48a6..6403eda2bbc 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java @@ -27,15 +27,21 @@ import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; +import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.apache.cxf.rt.security.crypto.MessageDigestUtils; public class MemoryClientCodeStateManager implements ClientCodeStateManager { private ConcurrentHashMap> map = new ConcurrentHashMap>(); - + private boolean generateNonce; @Override public MultivaluedMap toRedirectState(MessageContext mc, MultivaluedMap requestState) { String stateParam = OAuthUtils.generateRandomTokenKey(); + if (generateNonce) { + String nonceParam = MessageDigestUtils.generate(CryptoUtils.generateSecureRandomBytes(16)); + requestState.putSingle(OAuthConstants.NONCE, nonceParam); + } map.put(stateParam, requestState); OAuthUtils.setSessionToken(mc, stateParam, "state", 0); MultivaluedMap redirectMap = new MetadataMap(); @@ -53,4 +59,7 @@ public MultivaluedMap fromRedirectState(MessageContext mc, } return map.remove(stateParam); } + public void setGenerateNonce(boolean generateNonce) { + this.generateNonce = generateNonce; + } } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/IdToken.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/IdToken.java index ed5f7f431b3..7b0b1ad75be 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/IdToken.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/IdToken.java @@ -23,10 +23,11 @@ import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; public class IdToken extends UserInfo { public static final String AUTH_TIME_CLAIM = "auth_time"; - public static final String NONCE_CLAIM = "nonce"; + public static final String NONCE_CLAIM = OAuthConstants.NONCE; public static final String ACR_CLAIM = "acr"; public static final String AZP_CLAIM = "azp"; public static final String AMR_CLAIM = "amr"; diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java index 18d7e40e9c3..43ec050d95b 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java @@ -85,7 +85,7 @@ protected MultivaluedMap toCodeRequestState(ContainerRequestCont } private void validateIdToken(IdToken idToken, MultivaluedMap state) { - String nonce = state.getFirst("nonce"); + String nonce = state.getFirst(IdToken.NONCE_CLAIM); String tokenNonce = idToken.getNonce(); if (nonce != null && (tokenNonce == null || !nonce.equals(tokenNonce))) { throw ExceptionUtils.toNotAuthorizedException(null, null); From 9680acf2ea8b7b9bb08d5db6a07f91a12f26ccee Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 19 Nov 2015 16:34:10 +0000 Subject: [PATCH 0099/1346] More nonce related updates and making sure the OAuthServiceException mappers can be reused in case of nonce/etc validation issues --- .../oauth2/client/JoseClientCodeStateManager.java | 7 ++++--- .../oauth2/client/MemoryClientCodeStateManager.java | 4 +++- .../rs/security/oidc/rp/OidcClientCodeRequestFilter.java | 8 +++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java index e269d1952fa..18802b93934 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/JoseClientCodeStateManager.java @@ -57,11 +57,13 @@ public MultivaluedMap toRedirectState(MessageContext mc, if (theEncryptionProvider == null && theSigProvider == null) { throw new OAuthServiceException("The state can not be protected"); } + MultivaluedMap redirectMap = new MetadataMap(); if (generateNonce && theSigProvider != null) { JwsCompactProducer nonceProducer = new JwsCompactProducer(OAuthUtils.generateRandomTokenKey()); String nonceParam = nonceProducer.signWith(theSigProvider); requestState.putSingle(OAuthConstants.NONCE, nonceParam); + redirectMap.putSingle(OAuthConstants.NONCE, nonceParam); } Map stateMap = CastUtils.cast((Map)requestState); String json = jsonp.toJson(stateMap); @@ -75,15 +77,14 @@ public MultivaluedMap toRedirectState(MessageContext mc, if (theEncryptionProvider != null) { stateParam = theEncryptionProvider.encrypt(StringUtils.toBytesUTF8(stateParam), null); } - MultivaluedMap map = new MetadataMap(); if (storeInSession) { String sessionStateAttribute = OAuthUtils.generateRandomTokenKey(); OAuthUtils.setSessionToken(mc, stateParam, sessionStateAttribute, 0); stateParam = sessionStateAttribute; } - map.putSingle(OAuthConstants.STATE, stateParam); + redirectMap.putSingle(OAuthConstants.STATE, stateParam); - return map; + return redirectMap; } @Override diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java index 6403eda2bbc..33a95df448c 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientCodeStateManager.java @@ -38,13 +38,15 @@ public class MemoryClientCodeStateManager implements ClientCodeStateManager { public MultivaluedMap toRedirectState(MessageContext mc, MultivaluedMap requestState) { String stateParam = OAuthUtils.generateRandomTokenKey(); + MultivaluedMap redirectMap = new MetadataMap(); + if (generateNonce) { String nonceParam = MessageDigestUtils.generate(CryptoUtils.generateSecureRandomBytes(16)); requestState.putSingle(OAuthConstants.NONCE, nonceParam); + redirectMap.putSingle(OAuthConstants.NONCE, nonceParam); } map.put(stateParam, requestState); OAuthUtils.setSessionToken(mc, stateParam, "state", 0); - MultivaluedMap redirectMap = new MetadataMap(); redirectMap.putSingle(OAuthConstants.STATE, stateParam); return redirectMap; } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java index 43ec050d95b..7d9045702a7 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClientCodeRequestFilter.java @@ -32,6 +32,8 @@ import org.apache.cxf.rs.security.oauth2.client.ClientCodeRequestFilter; import org.apache.cxf.rs.security.oauth2.client.ClientTokenContext; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oidc.common.IdToken; public class OidcClientCodeRequestFilter extends ClientCodeRequestFilter { @@ -88,20 +90,20 @@ private void validateIdToken(IdToken idToken, MultivaluedMap sta String nonce = state.getFirst(IdToken.NONCE_CLAIM); String tokenNonce = idToken.getNonce(); if (nonce != null && (tokenNonce == null || !nonce.equals(tokenNonce))) { - throw ExceptionUtils.toNotAuthorizedException(null, null); + throw new OAuthServiceException(OAuthConstants.INVALID_REQUEST); } if (maxAgeOffset != null) { Long authTime = Long.parseLong(state.getFirst(MAX_AGE_PARAMETER)); Long tokenAuthTime = idToken.getAuthenticationTime(); if (tokenAuthTime > authTime) { - throw ExceptionUtils.toNotAuthorizedException(null, null); + throw new OAuthServiceException(OAuthConstants.INVALID_REQUEST); } } String acr = idToken.getAuthenticationContextRef(); // Skip the check if the acr is not set given it is a voluntary claim if (acr != null && authenticationContextRef != null && !authenticationContextRef.contains(acr)) { - throw ExceptionUtils.toNotAuthorizedException(null, null); + throw new OAuthServiceException(OAuthConstants.INVALID_REQUEST); } } From 7fdc34030bdcfaa4226c28ea8ce83f34563a5b6d Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 19 Nov 2015 14:27:39 +0000 Subject: [PATCH 0100/1346] Some changes to the STSTokenOutInterceptor to make it easier to subclass --- .../policy/interceptors/STSTokenOutInterceptor.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSTokenOutInterceptor.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSTokenOutInterceptor.java index 14b8b5f4bd0..f0683e0281d 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSTokenOutInterceptor.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSTokenOutInterceptor.java @@ -51,7 +51,11 @@ public class STSTokenOutInterceptor extends AbstractPhaseInterceptor { private TokenRequestParams tokenParams; public STSTokenOutInterceptor(AuthParams authParams, String stsWsdlLocation, Bus bus) { - super(Phase.PREPARE_SEND); + this(Phase.PREPARE_SEND, authParams, stsWsdlLocation, bus); + } + + public STSTokenOutInterceptor(String phase, AuthParams authParams, String stsWsdlLocation, Bus bus) { + super(phase); this.stsClient = configureBasicSTSClient(authParams, stsWsdlLocation, bus); this.tokenParams = new TokenRequestParams(); } @@ -79,6 +83,12 @@ public void handleMessage(Message message) throws Fault { if (tok == null) { LOG.warning("Security token was not retrieved from STS"); } + processToken(message, tok); + } + + // An extension point to allow custom processing of the token + protected void processToken(Message message, SecurityToken tok) { + } public STSClient getSTSClient() { From ade622bf89a6d72d1aca4ab3a82dc4450cd5a603 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 19 Nov 2015 14:40:57 +0000 Subject: [PATCH 0101/1346] Adding a system test for a JAX-RS service and JWT/STS --- services/sts/systests/advanced/pom.xml | 6 + .../systest/sts/jwt/DoubleItPortTypeImpl.java | 41 +++++ .../cxf/systest/sts/jwt/JWTUnitTest.java | 2 +- .../cxf/systest/sts/jwt/JaxrsJWTTest.java | 152 +++++++++++++++++ .../apache/cxf/systest/sts/jwt/Server.java | 46 +++++ .../cxf/systest/sts/deployment/cxf-sts.xml | 3 +- .../apache/cxf/systest/sts/jwt/DoubleIt.wsdl | 157 ++++++++++++++++++ .../{cxf-unit-client.xml => cxf-client.xml} | 0 .../cxf/systest/sts/jwt/cxf-service.xml | 53 ++++++ .../org/apache/cxf/systest/sts/jwt/jaxrs.xml | 26 +++ 10 files changed, 483 insertions(+), 3 deletions(-) create mode 100644 services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/DoubleItPortTypeImpl.java create mode 100644 services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java create mode 100644 services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/Server.java create mode 100644 services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/DoubleIt.wsdl rename services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/{cxf-unit-client.xml => cxf-client.xml} (100%) create mode 100644 services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-service.xml create mode 100644 services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/jaxrs.xml diff --git a/services/sts/systests/advanced/pom.xml b/services/sts/systests/advanced/pom.xml index bd288e51446..96b1b7d9a6f 100644 --- a/services/sts/systests/advanced/pom.xml +++ b/services/sts/systests/advanced/pom.xml @@ -54,6 +54,12 @@ ${project.version} test + + org.apache.cxf + cxf-rt-rs-security-jose-jaxrs + ${project.version} + test + org.apache.cxf cxf-rt-frontend-jaxws diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/DoubleItPortTypeImpl.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/DoubleItPortTypeImpl.java new file mode 100644 index 00000000000..e9b50aae2b4 --- /dev/null +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/DoubleItPortTypeImpl.java @@ -0,0 +1,41 @@ +/** + * 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.systest.sts.jwt; + +import javax.annotation.Resource; +import javax.jws.WebService; +import javax.xml.ws.WebServiceContext; + +import org.apache.cxf.feature.Features; +import org.example.contract.doubleit.DoubleItPortType; + +@WebService(targetNamespace = "http://www.example.org/contract/DoubleIt", + serviceName = "DoubleItService", + endpointInterface = "org.example.contract.doubleit.DoubleItPortType") +@Features(features = "org.apache.cxf.feature.LoggingFeature") +public class DoubleItPortTypeImpl implements DoubleItPortType { + + @Resource + WebServiceContext wsContext; + + public int doubleIt(int numberToDouble) { + return numberToDouble * 2; + } + +} diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java index 90da0c321e9..00ed2b18729 100644 --- a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JWTUnitTest.java @@ -61,7 +61,7 @@ public static void cleanup() throws Exception { @org.junit.Test public void testIssueJWTToken() throws Exception { SpringBusFactory bf = new SpringBusFactory(); - URL busFile = JWTUnitTest.class.getResource("cxf-unit-client.xml"); + URL busFile = JWTUnitTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java new file mode 100644 index 00000000000..890a1116f3d --- /dev/null +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java @@ -0,0 +1,152 @@ +/** + * 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.systest.sts.jwt; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.core.HttpHeaders; + +import org.apache.cxf.Bus; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.Phase; +import org.apache.cxf.phase.PhaseInterceptorChain; +import org.apache.cxf.systest.sts.common.SecurityTestUtil; +import org.apache.cxf.systest.sts.deployment.STSServer; +import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; +import org.apache.cxf.ws.security.SecurityConstants; +import org.apache.cxf.ws.security.policy.interceptors.STSTokenOutInterceptor; +import org.apache.cxf.ws.security.tokenstore.SecurityToken; +import org.apache.cxf.ws.security.trust.STSClient; +import org.apache.cxf.ws.security.trust.STSTokenRetriever.TokenRequestParams; +import org.junit.BeforeClass; + +/** + * In this test case, a CXF JAX-RS client gets a JWT token from the STS + sends it to the + * service provider, which validates it. + */ +public class JaxrsJWTTest extends AbstractBusClientServerTestBase { + + public static final String JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"; + static final String STSPORT = allocatePort(STSServer.class); + + private static final String PORT = allocatePort(Server.class); + + @BeforeClass + public static void startServers() throws Exception { + assertTrue( + "Server failed to launch", + // run the server in the same process + // set this to false to fork + launchServer(Server.class, true) + ); + assertTrue( + "Server failed to launch", + // run the server in the same process + // set this to false to fork + launchServer(STSServer.class, true) + ); + } + + @org.junit.AfterClass + public static void cleanup() throws Exception { + SecurityTestUtil.cleanup(); + stopAllServers(); + } + + @org.junit.Test + public void testSuccessfulInvocation() throws Exception { + + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = JaxrsJWTTest.class.getResource("cxf-client.xml"); + + Bus bus = bf.createBus(busFile.toString()); + SpringBusFactory.setDefaultBus(bus); + SpringBusFactory.setThreadDefaultBus(bus); + + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs"; + final int numToDouble = 25; + + List providers = Collections.singletonList(new JwtOutFilter()); + + WebClient client = WebClient.create(address, providers); + client.type("text/plain").accept("text/plain"); + + STSClient stsClient = getSTSClient(JWT_TOKEN_TYPE, bus); + STSTokenOutInterceptor stsInterceptor = + new STSTokenOutInterceptor(Phase.PRE_LOGICAL, stsClient, new TokenRequestParams()); + stsInterceptor.getBefore().add(JwtOutFilter.class.getName()); + WebClient.getConfig(client).getOutInterceptors().add(stsInterceptor); + + int resp = client.post(numToDouble, Integer.class); + org.junit.Assert.assertEquals(2 * numToDouble, resp); + + bus.shutdown(true); + } + + private STSClient getSTSClient( + String tokenType, Bus bus + ) throws Exception { + STSClient stsClient = new STSClient(bus); + String port = STSPORT; + + stsClient.setWsdlLocation("https://localhost:" + port + "/SecurityTokenService/Transport?wsdl"); + stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); + stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"); + + Map properties = new HashMap(); + properties.put(SecurityConstants.USERNAME, "alice"); + properties.put( + SecurityConstants.CALLBACK_HANDLER, + "org.apache.cxf.systest.sts.common.CommonCallbackHandler" + ); + + stsClient.setProperties(properties); + stsClient.setTokenType(tokenType); + stsClient.setSendKeyType(false); + + return stsClient; + } + + private static class JwtOutFilter implements ClientRequestFilter { + + @Override + public void filter(ClientRequestContext requestContext) throws IOException { + SecurityToken token = + (SecurityToken)requestContext.getProperty(SecurityConstants.TOKEN); + if (token == null) { + Message m = PhaseInterceptorChain.getCurrentMessage(); + token = (SecurityToken)m.getContextualProperty(SecurityConstants.TOKEN); + } + + if (token != null && token.getToken() != null) { + requestContext.getHeaders().putSingle(HttpHeaders.AUTHORIZATION, + "JWT" + " " + token.getToken().getTextContent()); + } + } + } +} diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/Server.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/Server.java new file mode 100644 index 00000000000..55e1585a4f9 --- /dev/null +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/Server.java @@ -0,0 +1,46 @@ +/** + * 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.systest.sts.jwt; + +import java.net.URL; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.bus.spring.SpringBusFactory; +import org.apache.cxf.testutil.common.AbstractBusTestServerBase; + +public class Server extends AbstractBusTestServerBase { + + public Server() { + + } + + protected void run() { + URL busFile = Server.class.getResource("cxf-service.xml"); + Bus busLocal = new SpringBusFactory().createBus(busFile); + BusFactory.setDefaultBus(busLocal); + setBus(busLocal); + + try { + new Server(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml index f33a137fdd0..490d5cf211e 100644 --- a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/deployment/cxf-sts.xml @@ -111,8 +111,7 @@ - https://localhost:(\d)*/doubleit/services/doubleittransport.* - + https://localhost:(\d)*/doubleit/services/doubleit.* diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/DoubleIt.wsdl b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/DoubleIt.wsdl new file mode 100644 index 00000000000..2166d33fd96 --- /dev/null +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/DoubleIt.wsdl @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0 + http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer + + + + + + http://localhost:8080/SecurityTokenService/UT + + + + + + http://localhost:8080/SecurityTokenService/UT/mex + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-unit-client.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-client.xml similarity index 100% rename from services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-unit-client.xml rename to services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-client.xml diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-service.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-service.xml new file mode 100644 index 00000000000..4c2ca113d85 --- /dev/null +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/cxf-service.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/jaxrs.xml b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/jaxrs.xml new file mode 100644 index 00000000000..dc68b359e6f --- /dev/null +++ b/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/jwt/jaxrs.xml @@ -0,0 +1,26 @@ + + + + + + + + + From 4702274c7a8bad71ab512744ad39f5207bfe86cd Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 19 Nov 2015 16:41:19 +0000 Subject: [PATCH 0102/1346] Fixing failing test --- .../test/java/org/apache/cxf/systest/sts/soap12/Soap12Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/soap12/Soap12Test.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/soap12/Soap12Test.java index de2a9ae5b03..beaf0ce90a0 100644 --- a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/soap12/Soap12Test.java +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/soap12/Soap12Test.java @@ -168,7 +168,7 @@ public void testFaultCode() throws Exception { try { String badAddress = - "https://localhost:" + PORT + "/doubleit/services/doubleitbadtransportsaml1"; + "https://localhost:" + PORT + "/doubleit/services/baddoubleittransportsaml1"; requestSecurityToken(SAML1_TOKEN_TYPE, BEARER_KEYTYPE, bus, badAddress); fail("Failure expected on a bad endpoint address"); } catch (SoapFault ex) { From 35bebef60de1221475534d6b1e5eeba643db6a0b Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 19 Nov 2015 16:41:29 +0000 Subject: [PATCH 0103/1346] Updating expired certs --- .../saml/sso/SAMLResponseValidatorTest.java | 1 + .../sso/saml/src/test/resources/alice.jks | Bin 1861 -> 4125 bytes .../security/xml/src/test/resources/alice.jks | Bin 1861 -> 4125 bytes rt/ws/security/src/test/resources/alice.jks | Bin 3984 -> 4125 bytes rt/ws/security/src/test/resources/cxfca.jks | Bin 891 -> 961 bytes .../src/test/resources/certs/alice.jks | Bin 2428 -> 4125 bytes .../src/test/resources/certs/bob.jks | Bin 2422 -> 4122 bytes .../src/test/resources/certs/cxfca.jks | Bin 1306 -> 961 bytes .../kerberos/src/test/resources/alice.jks | Bin 3984 -> 4125 bytes systests/kerberos/src/test/resources/bob.jks | Bin 3979 -> 4122 bytes .../systest/jaxrs/security/certs/alice.jks | Bin 3984 -> 4125 bytes .../cxf/systest/jaxrs/security/certs/bob.jks | Bin 3979 -> 4122 bytes .../systest/jaxrs/security/certs/cxfca.jks | Bin 891 -> 961 bytes .../src/test/resources/certs/alice.jks | Bin 3984 -> 4125 bytes .../src/test/resources/certs/bob.jks | Bin 3979 -> 4122 bytes .../src/test/resources/certs/cxfca.jks | Bin 891 -> 961 bytes .../src/test/resources/keys/alice.jks | Bin 3984 -> 4125 bytes .../src/test/resources/keys/bob.jks | Bin 3979 -> 4122 bytes .../src/test/resources/keys/cxfca.jks | Bin 891 -> 961 bytes .../src/test/resources/keys/alice.jks | Bin 3984 -> 4125 bytes .../src/test/resources/keys/bob.jks | Bin 3979 -> 4122 bytes .../src/test/resources/keys/cxfca.jks | Bin 891 -> 961 bytes .../ws-rm/src/test/resources/certs/alice.jks | Bin 3984 -> 4125 bytes .../ws-rm/src/test/resources/certs/bob.jks | Bin 3979 -> 4122 bytes .../src/test/resources/alice.jks | Bin 3984 -> 4125 bytes .../src/test/resources/bob.jks | Bin 3979 -> 4122 bytes .../src/test/resources/cxfca.jks | Bin 891 -> 961 bytes .../src/test/resources/certs/alice.jks | Bin 3984 -> 4125 bytes .../src/test/resources/certs/bob.jks | Bin 3979 -> 4122 bytes .../src/test/resources/certs/cxfca.jks | Bin 891 -> 961 bytes .../src/test/resources/certs/xkms/bob.crt | Bin 932 -> 932 bytes .../certs/xkms/trusted_cas/cxfca.crt | Bin 829 -> 899 bytes 32 files changed, 1 insertion(+) diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java index a5921c26b97..f33a63bb4d9 100644 --- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java +++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SAMLResponseValidatorTest.java @@ -462,6 +462,7 @@ public void testSignedResponseNoKeyInfo() throws Exception { InputStream input = Merlin.loadInputStream(loader, "alice.jks"); keyStore.load(input, "password".toCharArray()); ((Merlin)issuerCrypto).setKeyStore(keyStore); + issuerCrypto.setDefaultX509Identifier("alice"); response.getAssertions().add(assertion.getSaml2()); signResponse(response, "alice", "password", issuerCrypto, false); diff --git a/rt/rs/security/sso/saml/src/test/resources/alice.jks b/rt/rs/security/sso/saml/src/test/resources/alice.jks index 3a788c26e22bcda48505b19734f6978a068556aa..213b26cac3b24208121ced0297a8bd76ec16fb48 100644 GIT binary patch literal 4125 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 diff --git a/rt/rs/security/xml/src/test/resources/alice.jks b/rt/rs/security/xml/src/test/resources/alice.jks index 3a788c26e22bcda48505b19734f6978a068556aa..213b26cac3b24208121ced0297a8bd76ec16fb48 100644 GIT binary patch literal 4125 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 diff --git a/rt/ws/security/src/test/resources/alice.jks b/rt/ws/security/src/test/resources/alice.jks index 9b8c4503d2a960380a21d262427985464366b6db..213b26cac3b24208121ced0297a8bd76ec16fb48 100644 GIT binary patch literal 4125 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m 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 OoFL!5$uh-3-xvT`M3TS& diff --git a/services/xkms/xkms-client/src/test/resources/certs/bob.jks b/services/xkms/xkms-client/src/test/resources/certs/bob.jks index 26df583f43148e3796a856c5bb4c9e53e9128703..55509125b48976a2985a629d9cc9da6c7c5254b4 100644 GIT binary patch literal 4122 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 R?Cas{Jkyq557RT32LKwapL75K diff --git a/services/xkms/xkms-client/src/test/resources/certs/cxfca.jks b/services/xkms/xkms-client/src/test/resources/certs/cxfca.jks index accd45b229fee9040d07635fcf5792ddfba7351e..53ad2395202bc3fd3ebae89818352ca0304ef6fe 100644 GIT binary patch literal 961 zcmezO_TO6u1_mY|W(3o$$rWkIi9lYULU8ad2G$5YQv*u|2IgjiCgysBCZ_ZS%uI|- zOq>jB9@y1<(e+qjz{|#|)#lOmotKf3o0Y-9*ihI&kc~N%g-e(#u^=%yBULZIDBY09 zfD0tZEzF#nmu|>szysnj3q!0kkQ3)MG&M9dv;>morY2G1yhb3d8I((&p-qfR$c|%V zWngY%YjRF(U+eG0=c11fZdE&ex;4+^S=njdh(*h5Y6N>L4$n*d+q2hw#Zl(t zD-F+|IrT2=*{#;IM?8Dv!c-!oxAEkY1v_&Xv0a9 z%j5S>WS_Fy{t|Cp{+^kBZK-wt*15mP&OG0>?r+J7tc#QVeFbm#n$+|^ZMauv@>E?} zx%Oh#d3|%Mdx>`v8;`tbIFiErJ6!%ZOY&RgPaE8|&!$G(INn`Te(+q)=^L;0oJvf2 zr>;qS(6GuWG%myC&d*5~OPD?%n7Z!d+2c&ij0}v68>bpHPBM@MMu#jPix`WDqTdJo zzIIhDt9M^6?)g?eEBxs5PJ?8Sv@%PKK~w`)1q;oWn35GqO-Km|lq6(@Sy&C285#d0 z#~d(O0b`DlL2Aczw)Csh*|IbX970>3p5L{yEpW{ipABl#OT>?Cvu)0ulpG#+LRFqW zu^{i3m#eerC&8q7?$O8Nn3*T)aV#w6<6m+A?~;i7*H2#w%-q1RO}Wkf!d91O-%CZf z3&Y;dJUVMB`}N>8F_o}n)7#A^1wR|_bug<>ul+TdrG0 sHG11V?+$q4^yakc-gNI0Wk(&+$byr400O%(f&#ZN z4h9M<1_1;CDgqG!0R;dAf&!ztg=#qyS3Jx&PrvSV=2}jMQ)s;TcDF+t-lfPx^C5wr zL~?z57HaduS6^GHGLSf4UgJsLwtT3X(-(p7pi}O){KIyCjiA`V=d8dqTaMii7ElOc z9Jo>&(&R2zDSjgbr@FQ2yqjOdeE2-{=abpqw1BckmIAk0~9*l5PC`dn17Qu(YEzKws~;`Qg$z6i!Q153_w2$yRnVWTr$##k~4^(QTt&qcDUfx3b;ingEjV<46RG-F0#8#ob7=aCL7dLhQVQNC z0BaH?-1rW^0lfLeUTJ~OmqlGK)_Uxb+v!MXh~~9ABYnXXbp+rzkusULRX0mENtfZ> zw_{eIjTh*D?s{OGOHfFVu1j7IPLe@}U(rQq{=OMk+eliz zvkOpvDP*5f!esReYER_*YHStOMnAKF1XPXh07MDBmWp+@D{*0QD!3lAgRX|(g3FEqd>E(=R+T`Wg_k1f{N8@oB6?MXi!NS1RR~KHkg2d{z zq5O^sz4Eoxrn`9Kkr4k}dH+z&a_)Wr000311z0XMFgXAK0vRxZ0u?ZV0e+wZ0s#UO z0t87^U;r=;1_>&LNQUcJKq&4*=X?%4% z9vrw?0s{d60i!lBG%z6s163Uo1R5|Hf)-9sR9{9(2`Yw2hW8Bt0R;sB1A&163&7y!Fo6WYOzoZl4iI)|#TI{{ z;WR!nZ2#ZcOEpxMlrD>ZcxF7biQ2Fb2!^4vRgrZcEmu?ytGgiv$5^gV9-ZJCd-C|U7i;{Hwy6Ci1Vr&ZWvytHk-V70Xfc5x8 dURt^#N0V=AAbggmRNPgH$u;rnfV~6D6UJSQ7{~ws diff --git a/systests/kerberos/src/test/resources/alice.jks b/systests/kerberos/src/test/resources/alice.jks index 9b8c4503d2a960380a21d262427985464366b6db..213b26cac3b24208121ced0297a8bd76ec16fb48 100644 GIT binary patch literal 4125 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m 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 sHG11V?+$q4^yakc-gNI0Wk(&+$byr(m z0r8l5Sd%N#k`oQ&#CZ)(4GoM83@l9z3=N{hd2N7vLqkg_pF~q9URAHZW3%H?kgoWA z^Yr$qpCc5j9A+PhOg*u~4R^BJue}wV7Xk?LKZMer7hC z^T8iJ!XclL(j}Rp0u3p<+j>dw)igT)w34nKbW;N z>&fpA25*lG)~4qE&nl0dZ@;_d@wEDEUPjH$><>=7`Tum{T2+CGMk|U6wpH}SKIhY| zc91{xq99FW z?SWy+)K!d;&-cy?{&=`*UxV+l>wYX{hf3$&6Yg!i&zWcK(ouBoT=)D3uP5!jr*h>~ z`&MsGP9|nX2FAq!27U&zz`&N}V-aH!slWeozE)m*&G+q_UVLQ9ob=<{j${LQkhC(3 zgn?KCRt1y0nUv}+EF%{QN8KrxDmmJH%jl;p_nQkJ4%nrI`9GV*diBKLEk@}N(?6Lq zYQHi1u*Unjz>aSnE%WcohMKHt4SQ!SZqxUE#hL~0*Y{^G`t$p*^}Oea7S`6AZNgQK zEoZ5pHThl+!<;M3HhtHBUUsdX_bcq%ow-k!Wq#>5@GqU%x16tkfmnTUm9V#2f{r?4 z|FpliT)CR+r_QeYwMtuJR`inemH#RQjdnab9@1GE`b|KT`|*d0R~qZqAJaVa;)VBw zD}V1q*Y2NgbI2+>f0G5$s6FUL1W{NuiHdQ+BeLIv2)?E_*sHXdU?wa6*kz3Wj}`NvcU<(* z7uR%)JoYF-<6J-n7@*IyGo>@4JxpKs*erGB9+|bDp%z^v;!n+@@7_;D?q5;E5YFYb zTM_Um1lO(6^vDPZy`Z9gyEq$kTTl=eWe*dx0w!0J$N^6~1hR1x);#Zo_mP5X2UR3= z=TcjCFk=P-RUIP)TrgUI6d_LVFOG*LDmw1;(Y*F|npef=iJ>>7H83-gb|e}OFboC* zRRjYQ1!H(-V_|{`0Ic9XeDp0yq?3>W6n`n>MXkBUTd4QL5xGDwbXbUQf8`r!6n@5B zBo+<%GXT9hs-~&GAuxo0LSEO-b7$2d;KttQj4Wwq9TEYsTq~Tnnw(1<-#;WHaWdQG zn1lqqF`q)DA~?Y1qLv`h=s%`pY1k#da>Ge-=Tu_fflalNOOd3oS|{ochG?7ei+{T= zhkzL4|}NQuDjUKm_>Jumqi3_q$ekEpUIUdW9ewh%z zmn%>SZ1X40F3!;C%E-5w1@peVId2jGiQuW9_JSLoiKjqWJ=}n+`w0lpo@RATfN;Y1 bfuyT>Mu`!8|Mw8byrR@7u!!gGr~>1?wJJW< delta 723 zcmV;^0xbQc2c!oOFoFZ1FoFVzpaTK{0s;Xdkq{aI43U5oHw*)1Zf7wLFboC*RRjYJ z1!H(-V_`5J7Y#8rF)%VPFgZ0gGB#Ql4Kg(`FflhUIW;yiHd-(-ks&0Jnl68YjtAb| zMHLdh!c-5ao$YG;AhJ{aU^htArFZq_8t6xkOopv85+tn@$jA}$Nh>JPy9wVuv|sR5 zOpO!Eqti=!TJ;>q6)mT!XE;OEXevf9B+`7Q1l{wbR0GE+IRs`RWVqnYV>`l8*6ndV zK_sPqH%HJxC>eMMCM#&M4*Y+vt>Pw7V6G#3ISYx(c?W?Q%rn-Cz;tZ?FZA3Qr!pOK zt7FF2n{HZP#`z6L#Y@djF&X4SyG6cgESUl{&f2=+z*uGYX9*B@~jM%t7xNFS^y zkPaTXx}p5Y691khZp2^FpF)mWZ{I=$JObt;l05XENp4;$e!lO1?G*wsPm{<2PeoBI z|F$b3`Y02D#qekjp`VTOirX+_1_M^ZeM)&x3cK*1Zk4^_PArA zH>5Q%Gcgk|5e5TQ1PTn1fD|AM17&V!F%B>c1_M=ItBU@rXwPxvGVX)G4tm;JRlykFl%x}T5_U+oH(zcsiC2vC6F{X zHHi}EH3D(Xpj`3{ZDLeHb{r!s19KB2KZ8LNBNtN>BO}8xvsgnmUG=lSr~KA=A~IFs z&3&iqHFlEBhCM;gnnE5evJhMQtiDsu=GP%#lXGJGT7M@#7kzwitJ?9?t$7~L%1--6 zELvVuBiLJUcwXw?p1tlXjxrx#X?Xt3sdr(|Znd60;@Kk?rV<&|{mIXI`;J>0j|5i# z);sb0QSv=O_fI-a%MR;A8%~;B9=~@Y`;^u8mw4;)_ssNbORf92&izGp=J~F5e@jke zU7YOiD|oxtq^AFA!@V+-r|QbewHLF_>ziBMOT3fVc;rRHkrd|N;qt#(lHV$S+TgBz zHZ|JD@$Q=PgXd~a-*~O(RAS0Ibxq=fhE-0XaTzXmeoner!u0vT)O9D%9%o`^WMEv} zIMtwWl7TERI%N4+#8^ZW{XXdTwX14bz58-;&$sef;YXi$8YF|Hm04m8q8hL&SZKb) zl&nZZFBCVva<_3ms%5C-+wz@p~UMj*}81{DN(OFB`um7Ixse5PEvK*V(ZyfGYRZDkB%Diz} z#kgBt=)r3LITfe7g$yq8RBotxIrIGm%~@|Je44gk$-K8_JGbh}OJ6hJzwS}Va?!N! ze;H>XwirhAttJL&)c;SFFh literal 829 zcmXqLVzxABVp3ed%*4pV#L4jbqh-{McEbz4rQ8Tp&Si9_G}%bVEJ^9uSY2hc&q(EjiIZPMp`!)X>1lz`)Yf zz|bHH%r!K$gmTFQ}l49rc8{0s(7j9g4jjEoF-Y<4^f(iNX?p58w7bA)1* z!|WrGspqyu^Zqiicy;$vpwn*V=>mr}7Rt0#BtE~WHuLMR-N((u&&+0XKKR3@{HTHX z6w}|u8i&-^S(j?F8LszreYEjx=$U!Pla{l++*Vu57T+bkde*}H2eX!DJ^B5?;O%k2 z+SJ_tS>>_w?RVEao|etasJWT_!HGBjpH5t>DlpM#MNz@FioV$Ae7e;R@`s+3&$y#{ z_r?pA^Jz)V^W%#+dLpK-NS9pu@{jJp)lUz*?KWL_MDgsrGg~bkHixM_Fie@ciZSx} z-g&_v4_EDL@I7|jkEQHT>AZWwy^Z%d^Q>Juiq4(up8w$Wq`mi4uAFM$%E`&Z%*epF zIKaTqKo%GovV1IJEF$&yf6mv+i?8{u8yOUpCZaO>5XYV{x0l_bb*cc)z|sbJ3sQf34>|PqeVM z-fR=Da%?$E{jACNav0`ZVYcbJ{`0bH^}JtU-|ozPx-9cczkz@0#J=Tx{R_m3tAxGH z5_HrV`=|ZA<;vAmKXrEHuT|O-v!a)zul!djXtd+e@sQ5S&~E~w+>bw0ywX^={+Q;W z7caagT={z^x_19`n?qL7A;&UR)})>nI>8e+E5E4J|5w%JxmAl_Y Date: Thu, 19 Nov 2015 17:52:27 +0000 Subject: [PATCH 0104/1346] Fixing some failing tests --- .../jaxrs/security/certs/jwkPublicSet.txt | 6 +++--- .../src/test/resources/sts.jks | Bin 3980 -> 4121 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt index 87f573308bc..9313284e905 100644 --- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt +++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/certs/jwkPublicSet.txt @@ -16,15 +16,15 @@ "kty":"RSA", "kid":"AliceCert", "x5c": [ - "MIIDojCCAoqgAwIBAgIBIDANBgkqhkiG9w0BAQsFADAzMRMwEQYDVQQKDAphcGFjaGUub3JnMQwwCgYDVQQLDANlbmcxDjAMBgNVBAMMBWN4ZmNhMB4XDTE1MTAyMDA5NTM1N1oXDTI1MTAxNzA5NTM1N1owMzETMBEGA1UEChMKYXBhY2hlLm9yZzEMMAoGA1UECxMDZW5nMQ4wDAYDVQQDEwVhbGljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMSBK+IlFaDphtJa96Vjt5et9KMEw/dIzDe+OfZQjAUSkZ1FQoWDaIOkrnbXq/7jfz7dgOx0QS9AMrgh7sEMnd0NGm1tmQr12Zxb9CIkIVFKTBQseCnJGn4Qctt24GVROqlogXs8/Og2NeSa8XJhnUwJFPoVG1QDHswVANlE6jS9TOXZG6fN5TyRwZwrzRVrcbDfUxV+t+HOTBeI/hrFlrJLYfPohZsGIckZvO7AwT6BH9A32L04HfZLGHFwtIjFKTXueC2R8BJZl/HQ8ctwr50L4wytowcpiQT1viJU9gj01xMkRR9wJ+ybenpciQw22wNn2BQ48B3t749Xg8h6v1MCAwEAAaOBwDCBvTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUcKtIwhA3fgWTH49HUGN0W2Jlbj8wYwYDVR0jBFwwWoAUf9/5nypuX3z3t7Lo8QRpkvj2uGOhN6Q1MDMxEzARBgNVBAoMCmFwYWNoZS5vcmcxDDAKBgNVBAsMA2VuZzEOMAwGA1UEAwwFY3hmY2GCCQDr8Tla2IcxaDANBgkqhkiG9w0BAQsFAAOCAQEAPnReJBRXZHTyKTt1Z/k+nLjALGO5h8pejvmqoOt8/i9jMghS+5Pdjel8b9+RFofdC7i88pUHt+ZGrZvYEFXl/+UJFWjPt/X/QwrWKbDT95iFPJOSJxk0XL15HS7uKqEWaF2O9EOHndg5XR6YFYuSkHLA6PSsWVXsfgQ4WhTHgcSXz7pgeh7gdp8ItLJ7mBcqN1Jk94yd5BiEfo8Woyh+TVaFoWZcIgN2MfRTk9B75EWrkw5UsUoJ6/Dpq3+kqz+81DfUfTsmKgPWoT3y4UBSnPkFhF7uWguVKd/jUb6StXiNEIrwHYDxzJzBXK1nAFPnNQl+OzDE8J1BPf1pi/acAg==" - ] + "MIIDojCCAoqgAwIBAgIBIDANBgkqhkiG9w0BAQsFADAzMRMwEQYDVQQKEwphcGFjaGUub3JnMQwwCgYDVQQLEwNlbmcxDjAMBgNVBAMTBWN4ZmNhMB4XDTE1MTExOTE1MjExN1oXDTI1MTExNjE1MjExN1owMzETMBEGA1UEChMKYXBhY2hlLm9yZzEMMAoGA1UECxMDZW5nMQ4wDAYDVQQDEwVhbGljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJEtSxj+Fj6DUu8pSm1PaQxWOQLfTjTS3f5S1xD+HZ23oQE9q0gJ1tmcmGoi8EGYd6uC2YTLo8mcAya9pvxiXNPhbkzm6XvQbmvKKjMVe3MOm0OMZu64UgbFcuDxQ5yTHbJbq/sODUUE+AzlvkEiSceibg8LjjVwhWApR39yTDyVoUwtWC3hKUgAaRh1pRkcGJY5/hu9zPiKWxpApvjcRKW8e6EDP5+HJtEfv4FAulXyuN3NWlA+BTzhU3vCcFeUSK8GyJ2EYe7jU7escnn6VOU31YiZlwf4L+nlcShrssBU+QS7t0e1tnx39XwYPnMMfk3IJ5XHrzWELamDFzJUANsCAwEAAaOBwDCBvTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUdtunjeY0on5gtDZ464z81hD/RR4wYwYDVR0jBFwwWoAUIU7wL46HJSo67vTRvPZ3mlfF54mhN6Q1MDMxEzARBgNVBAoTCmFwYWNoZS5vcmcxDDAKBgNVBAsTA2VuZzEOMAwGA1UEAxMFY3hmY2GCCQCs4D589C1IpDANBgkqhkiG9w0BAQsFAAOCAQEAH5/3uv40Hif/AjEgLtCNm+V8B2zszugwJWS/0aCJkb/Qj22XnOSJ6kmBHkBvlJ70el2SmrW+ZysZo+II+qds663wsfrzBv4egnSNWRFBPeAhYdGNAAaqAbDduRHa4vUdmcYTHEl/EZCabQSr7VH1+L6yCvwbnhDf8LZVDrFLcTeNOqhQnN/vUaG1wu8csrTLuzZzEZ5YF8bBJQmlN9s7J1DzM60TgfrNJcCCYalFBQspQmnlFIqVoJC5n88GOUzcCCQ3YoT1zDqlVuJhasW2PoD3C0NRkFXdu9268xNG/lLgf+mcX2jEzfHAzb8+sxZKReBfE8T8QBIBd+GW6vRshA==" + ] }, { "kty":"RSA", "kid":"BobCert", "x5c": [ - "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" ] } diff --git a/systests/ws-security-examples/src/test/resources/sts.jks b/systests/ws-security-examples/src/test/resources/sts.jks index 3cdccfee85ee18eaa82ec836d0e5444e88eae056..880463d4a35907fcfb0be1a26ee01cef973eba86 100644 GIT binary patch literal 4121 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 literal 3980 zcmeI#S5OpL+XwLJnIQ}eNR}W;a?&1hQnDx@A~}c%!vG^ua*!oBAPSO`WRMIBNR}WO zT!$=*NKOL_C`pj~qHbARU+wo^?0dTxeev(^>OOVObGm=e@!Ihk1OkEnI*8ETHfT!- z1kk|^ePM?X8DEe9WgrmPB`_E!0|rAmvtUpF3PnIxHe^gkAEN9)3Sxv{=+)3&3IHG{ zf`DQuT9BF;W&kHcl3BV~qU|sjoLy~ENRaINNpd6%<7A7X1d-np4EghQAlq3AlmrSC z1wk1J5Cxk4h$xiIpYfkPG#EJl*TDfq5Lhs91_A~HWYAy$fQ-x6&FYGr&6Kizn6PEc z>8qI9VS?$aF{9WQm;Pg7^QLkGEQzX}Kl>cc+mbNI`}FI6L$~Bvd@8X@>o+a0PEaad z;=ql+jqin=hY&HU?7G@)MXv!qquZ*0c)7;kpV;CtOG!$$^K?qy-T8xcaIKr#ALDp( z*ULOpq2c~qqCEv5P)Rb~vwE@+^PGn&Mzqk?rN%qboIok!r^wzm@0C2xGcmfv-EU*{ zAk)H?G{QKc(3a`>@}Wp;$wK>;28rwr&fW}swTxn=A@4NGI-wXaAv9&^ZM6F~2VU>` zpbhtI^T?Q_eG)W2MrRC%*<&C(z3g7`Lagz>^w0AK1$PatN zYJM5}hCAcw3;EdXDDZ|yOk@FNR2GAqFRiBJ9brBoD)DGcjVw4IA=PKUScoyjEZ4T^ z*oRuQZnj(hk&nR+6+QXfy7wRaFUqYbFLWh|sjYcYhv+cvYnkH+ z@6TiTk?v>m9ok|R3yett$fQO1lO5+SK42)_^Qx@dj9T8Axo>ork0=e~Ur!xq`57j8 z_A3*LQqi1!Sprvz1Z|PGH;ab{FI6DN71|wG3rBcvt>$>=;lwv_poG;itfc%Nq*5qFqu z!kO$dy{#&Tx;>LB0us|(Sc7i43Ns9T`9!)p+QPN-z}!6%V=Df%@AZ;#5FzU2dJUu4 z+e|%K>XHtIYlU%&!pv~~=)<+oHa2}e!UJTDzp06LVz)FExYy5xQOUljjs@(ag1eEI~*eEBS`!oW_lSXWiYtH{F9o?Uy4BzeV?z{M8aJYHyNlvtQMIJXA+J;fmFye3-6B)i9g~u7li%*q zn&7YXr=aDz;@J0;MmMXm=@1=~mddiBu3Ehz&sKAci0DhW|p>uC*5ip zJ|8Qnc5bHQH)ly2>B?LBc$I;6yu>_FK!GA2!*yr&$^(9kIf=`-1}MuzLSHNtw|41` zg)B9jvOZH%x9_2<#bj1T%afMK`z zz&n7OmMG8_^CCTn9lrWnJYO4E%m0X{RJt&7}9<8>3#Qp15QXzcZz0bKs3eT*NBy8cx&u zF@_pktA zTVy6#XAWa6V8O{TSs5*ecf1)O$?}|t$`2>vz|69EsPdsfqM&Wd%7k2X%DFgguD^$H za(*V61i|Hby^VuQpVW=&x89I1K1(>t$C9Pc-13Q4R9W(M^>i*<-diZUg%rxYidTyt z{T*#COfANYg{C?tYcS)KF6S`0NG>O0Q}=jqoNI8?sJD97^v8JVcHbuXD5E&XY41u~ zn*Dmzrqyb*Fe5z3qC61N8=5+HafFb3dz9`&d&pBenZ9QSaQ%x5(@N0^8#URJP}ve0 zLcTxB$V2XYe6g0&p#08oe-*yS(Qnz!HhAVzIbuQ+lgT(&?p*T7sk@|krU#f}Qta!kC5kMSwlhwB=^}NAcgFuf z$1h>zo(d!DR2X4Dg#mE=pC8FTL%=Utpg=K6anVy*h@L_NCG}?nQNOrC`*&QS{lS$# z@dBg${s}+z!+&>0&@4SWhEAgi)%18GY;4k^MU9(5ilCdzSM#xxCjFc$&yt_ILTT$n zx%@^DTmjn=<1TYf4mN zM=o4nWshJ7PqnyRrR-(ULqI&ct2xud+)fQ4Y184AK4Fk6Y~$OSn&?o8De6(|%@pA7 zS158ZQF@Pl!|AiNQsupkS=ZrwKTb(c7JDNKGIbKZ<&wriL3%XENW^eIeK_Fh0HxOC zeJi)k2TAvqf4U;51q^Be5kF-B1tFj?G2%|>RUq`6GC%=mXJEbIf^pK*)8bIaIAL5Z z-7(f2Di~LHtPK`@8va5OBTI5*;w+%w?c32)rnbvIt!p$7qJNU)7HInSB*~V_mH0;m z6a4?m_6KArTeH)iTEriW)k!G^YYt$Sit#c|F3Ir}F(9Af-ohaPrxM1%JxAAU5j zzwY`qOS3kw0F#nYFQd$=Lk##nOn+iE3UbLZSz&n|QT!Qh#UyF3GU*_yU?u qSF73}>feUo--h7dhTwm12%_7Ds?776vuWaZ15pB{g>b(R-v0o@8livy From c8887c9b250d53148b7c0f59b5f55dbd34f02a80 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 19 Nov 2015 20:18:12 +0000 Subject: [PATCH 0105/1346] Fixing failing STS test --- .../java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java index 890a1116f3d..76fcb6e398c 100644 --- a/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java +++ b/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/jwt/JaxrsJWTTest.java @@ -20,7 +20,7 @@ import java.io.IOException; import java.net.URL; -import java.util.Collections; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -91,7 +91,8 @@ public void testSuccessfulInvocation() throws Exception { final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs"; final int numToDouble = 25; - List providers = Collections.singletonList(new JwtOutFilter()); + List providers = new ArrayList(); + providers.add(new JwtOutFilter()); WebClient client = WebClient.create(address, providers); client.type("text/plain").accept("text/plain"); From 296ce494aaca47e362fee3f736da20125a0ce1d8 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 19 Nov 2015 21:28:00 +0000 Subject: [PATCH 0106/1346] [CXF-6676] Adding a test --- .../wadlto/jaxrs/JAXRSContainerTest.java | 48 +++++++++++++++++++ .../test/resources/wadl/testComplexPath.xml | 13 +++++ 2 files changed, 61 insertions(+) create mode 100644 tools/wadlto/jaxrs/src/test/resources/wadl/testComplexPath.xml diff --git a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java index 5c0ab3401da..bbcde207636 100644 --- a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java +++ b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/JAXRSContainerTest.java @@ -28,7 +28,10 @@ import java.util.List; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import org.apache.cxf.helpers.FileUtils; @@ -405,6 +408,51 @@ public void testQueryMultipartParam() { } } + @Test + public void testComplexPath() { + try { + JAXRSContainer container = new JAXRSContainer(null); + + ToolContext context = new ToolContext(); + context.put(WadlToolConstants.CFG_OUTPUTDIR, output.getCanonicalPath()); + context.put(WadlToolConstants.CFG_WADLURL, getLocation("/wadl/testComplexPath.xml")); + context.put(WadlToolConstants.CFG_COMPILE, "true"); + + container.setContext(context); + container.execute(); + + assertNotNull(output.list()); + + List files = FileUtils.getFilesRecurse(output, ".+\\." + "class" + "$"); + assertEquals(1, files.size()); + assertTrue(checkContains(files, "application.Resource.class")); + @SuppressWarnings("resource") + ClassLoader loader = new URLClassLoader(new URL[] {output.toURI().toURL() }); + + Class test1 = loader.loadClass("application.Resource"); + Method[] test1Methods = test1.getDeclaredMethods(); + assertEquals(1, test1Methods.length); + assertEquals(2, test1Methods[0].getAnnotations().length); + assertNotNull(test1Methods[0].getAnnotation(GET.class)); + Path path = test1Methods[0].getAnnotation(Path.class); + assertNotNull(path); + assertEquals("/get-add-method", path.value()); + + assertEquals("getGetaddmethod", test1Methods[0].getName()); + Class[] paramTypes = test1Methods[0].getParameterTypes(); + assertEquals(1, paramTypes.length); + Annotation[][] paramAnns = test1Methods[0].getParameterAnnotations(); + assertEquals(String.class, paramTypes[0]); + assertEquals(1, paramAnns[0].length); + PathParam test1PathParam1 = (PathParam)paramAnns[0][0]; + assertEquals("id", test1PathParam1.value()); + + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + @Test public void testCodeGenWithImportedSchemaAndResourceSet() { try { diff --git a/tools/wadlto/jaxrs/src/test/resources/wadl/testComplexPath.xml b/tools/wadlto/jaxrs/src/test/resources/wadl/testComplexPath.xml new file mode 100644 index 00000000000..372ca850313 --- /dev/null +++ b/tools/wadlto/jaxrs/src/test/resources/wadl/testComplexPath.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From ab063a7cd6321d471dd0f28fd9b4eff22a6b86e1 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 20 Nov 2015 14:40:21 +0000 Subject: [PATCH 0107/1346] [CXF-6688] Updating ClientProxyImpl to do the best effort at mapping BeanParam bean's PathParam values to class-level template vars --- .../cxf/jaxrs/client/ClientProxyImpl.java | 28 +++++++++++++++---- .../cxf/systest/jaxrs/BookStoreSimple.java | 22 +++++++++++++++ ...ntServerResourceCreatedSpringBookTest.java | 19 +++++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java index d41721f2c70..37b798f87c3 100644 --- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java +++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java @@ -398,12 +398,32 @@ private List getPathParamValues(Method m, OperationResourceInfo ori, int bodyIndex) { List list = new LinkedList(); + + List methodVars = ori.getURITemplate().getVariables(); + List paramsList = getParameters(map, ParameterType.PATH); + Map beanParamValues = new HashMap(beanParams.size()); + for (Parameter p : beanParams) { + beanParamValues.putAll(getValuesFromBeanParam(params[p.getIndex()], PathParam.class)); + } + if (!beanParamValues.isEmpty() && !methodVars.containsAll(beanParamValues.keySet())) { + List classVars = ori.getClassResourceInfo().getURITemplate().getVariables(); + for (String classVar : classVars) { + BeanPair pair = beanParamValues.get(classVar); + if (pair != null) { + Object paramValue = convertParamValue(pair.getValue(), pair.getAnns()); + if (isRoot) { + valuesMap.put(classVar, paramValue); + } else { + list.add(paramValue); + } + } + } + } if (isRoot) { list.addAll(valuesMap.values()); } - List methodVars = ori.getURITemplate().getVariables(); - List paramsList = getParameters(map, ParameterType.PATH); + Map paramsMap = new LinkedHashMap(); for (Parameter p : paramsList) { if (p.getName().length() == 0) { @@ -417,10 +437,6 @@ private List getPathParamValues(Method m, } } - Map beanParamValues = new HashMap(beanParams.size()); - for (Parameter p : beanParams) { - beanParamValues.putAll(getValuesFromBeanParam(params[p.getIndex()], PathParam.class)); - } Object requestBody = bodyIndex == -1 ? null : params[bodyIndex]; for (String varName : methodVars) { Parameter p = paramsMap.remove(varName); diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreSimple.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreSimple.java index a046a4276b3..46ee30c7f45 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreSimple.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreSimple.java @@ -20,12 +20,29 @@ import javax.annotation.PostConstruct; import javax.annotation.Resource; +import javax.ws.rs.BeanParam; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @Path("/simplebooks/{id}") public class BookStoreSimple { + public static class BookBean { + private long id; + public BookBean() { + + } + public BookBean(long id) { + this.id = id; + } + public long getId() { + return id; + } + @PathParam("id") + public void setId(long id) { + this.id = id; + } + } @Resource private Book injectedBook; @@ -46,4 +63,9 @@ public void postConstruct() { throw new IllegalStateException("Book resource has not been injected"); } } + @GET + @Path("/beanparam") + public Book getBookBeanParam(@BeanParam BookBean bookBean) { + return getBook(bookBean.getId()); + } } diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringBookTest.java index 58736efe9d4..efa661fb19b 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringBookTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerResourceCreatedSpringBookTest.java @@ -25,6 +25,7 @@ import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.io.CachedOutputStream; +import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.model.AbstractResourceInfo; import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; @@ -66,6 +67,24 @@ public void testGetBookSimpleMatrixMiddle() throws Exception { assertEquals(444L, WebClient.create(address).get(Book.class).getId()); } + @Test + public void testGetBookSimpleProxy() throws Exception { + + String address = "http://localhost:" + PORT + "/webapp/rest"; + BookStoreSimple bookStore = JAXRSClientFactory.create(address, BookStoreSimple.class); + Book book = bookStore.getBook(444L); + assertEquals(444L, book.getId()); + } + + @Test + public void testGetBookSimpleBeanParamProxy() throws Exception { + + String address = "http://localhost:" + PORT + "/webapp/rest"; + BookStoreSimple bookStore = JAXRSClientFactory.create(address, BookStoreSimple.class); + Book book = bookStore.getBookBeanParam(new BookStoreSimple.BookBean(444)); + assertEquals(444L, book.getId()); + } + @Test public void testGetBook123() throws Exception { From c8f54d8c38bfdd186a245fce865e7f32a5e01856 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 20 Nov 2015 17:12:29 +0000 Subject: [PATCH 0108/1346] Fixing HTrace demo README typo --- .../src/main/release/samples/jax_rs/tracing_htrace/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/src/main/release/samples/jax_rs/tracing_htrace/README.txt b/distribution/src/main/release/samples/jax_rs/tracing_htrace/README.txt index c246b6d3c99..89b97f1cefb 100644 --- a/distribution/src/main/release/samples/jax_rs/tracing_htrace/README.txt +++ b/distribution/src/main/release/samples/jax_rs/tracing_htrace/README.txt @@ -1,4 +1,4 @@ -JAX-RS Search Demo +JAX-RS HTrace Demo ================= The demo shows a basic usage of HTrace distributed tracer with REST based From 60f95e5c0d928fd0b518bac1b89e20b9a12e09b3 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 20 Nov 2015 21:36:49 +0000 Subject: [PATCH 0109/1346] Making ClientTokenContextManager available on the current message and having OIDC authorization service keeping the super path in case the actual /login is managed by another endpoint --- .../rs/security/oauth2/client/ClientCodeRequestFilter.java | 1 + .../rs/security/oidc/idp/OidcAuthorizationCodeService.java | 3 --- .../apache/cxf/rs/security/oidc/idp/OidcImplicitService.java | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index f712ab67038..b52e92b7ad1 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -195,6 +195,7 @@ protected void processCodeResponse(ContainerRequestContext rc, ClientTokenContext tokenContext = initializeClientTokenContext(rc, at, state); if (at != null && clientTokenContextManager != null) { clientTokenContextManager.setClientTokenContext(mc, tokenContext); + JAXRSUtils.getCurrentMessage().setContent(ClientTokenContextManager.class, clientTokenContextManager); } setClientCodeRequest(tokenContext); } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcAuthorizationCodeService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcAuthorizationCodeService.java index e1e7c3f3e8c..99fda9fa616 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcAuthorizationCodeService.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcAuthorizationCodeService.java @@ -20,13 +20,10 @@ import java.util.List; -import javax.ws.rs.Path; - import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; import org.apache.cxf.rs.security.oauth2.services.AuthorizationCodeGrantService; -@Path("/login") public class OidcAuthorizationCodeService extends AuthorizationCodeGrantService { private static final String OPEN_ID_CONNECT_SCOPE = "openid"; private boolean skipAuthorizationWithOidcScope; diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java index c6638e3b2d2..66e5e8b23fc 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcImplicitService.java @@ -22,13 +22,11 @@ import java.util.HashSet; import java.util.List; -import javax.ws.rs.Path; - import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; import org.apache.cxf.rs.security.oauth2.services.ImplicitGrantService; -@Path("/login") + public class OidcImplicitService extends ImplicitGrantService { private static final String OPEN_ID_CONNECT_SCOPE = "openid"; private static final String ID_TOKEN_RESPONSE_TYPE = "id_token"; From 23e39b89406fa4dc1654c074dcb705f5bad062a6 Mon Sep 17 00:00:00 2001 From: Akitoshi Yoshida Date: Mon, 23 Nov 2015 11:03:58 +0100 Subject: [PATCH 0110/1346] Align xalan's osgi and non-osgi versions --- parent/pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/parent/pom.xml b/parent/pom.xml index 6b3c040916b..210c0b0128e 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -187,7 +187,8 @@ 1.2_5 2.11.0_1 5.2_4 - 2.7.1_7 + 2.7.2 + 2.7.2_2 2.0.8_6 1.6.1_5 1.1_4 @@ -998,7 +999,7 @@ xalan xalan - 2.7.2 + ${cxf.xalan.version} xml-apis From f4983a20099dfbf07592ec9b219f3f900159ae32 Mon Sep 17 00:00:00 2001 From: Akitoshi Yoshida Date: Mon, 23 Nov 2015 11:23:17 +0100 Subject: [PATCH 0111/1346] make feature cxf-ws-security installable in karaf --- osgi/karaf/features/src/main/resources/features.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osgi/karaf/features/src/main/resources/features.xml b/osgi/karaf/features/src/main/resources/features.xml index da1f799ffa4..dafdbeab722 100644 --- a/osgi/karaf/features/src/main/resources/features.xml +++ b/osgi/karaf/features/src/main/resources/features.xml @@ -118,6 +118,7 @@ cxf-ws-policy wss4j cxf-ws-addr + mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/${cxf.geronimo.transaction.version} mvn:net.sf.ehcache/ehcache/${cxf.ehcache.version} mvn:org.apache.cxf/cxf-rt-ws-security/${project.version} mvn:org.apache.cxf/cxf-rt-security-saml/${project.version} @@ -308,7 +309,7 @@ cxf-core cxf-bindings-soap - mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1 + mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/${cxf.geronimo.transaction.version} mvn:org.apache.geronimo.specs/geronimo-jms_1.1_spec/${cxf.geronimo.jms.version} mvn:org.apache.cxf/cxf-rt-transports-jms/${project.version} From 577dd538a026b339c4a5a1c438a41b5e2d04e184 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 23 Nov 2015 10:53:52 +0000 Subject: [PATCH 0112/1346] Adding OAuthInvoker which can react to the expired access token token by refreshing the token and reinvoking --- .../client/ClientCodeRequestFilter.java | 6 +- .../MemoryClientTokenContextManager.java | 9 ++- .../security/oauth2/client/OAuthInvoker.java | 71 +++++++++++++++++++ 3 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index b52e92b7ad1..daaf1212c52 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -307,10 +307,8 @@ protected ClientTokenContext getClientTokenContext(ContainerRequestContext rc) { if (ctx != null) { ClientAccessToken newAt = refreshAccessTokenIfExpired(ctx.getToken()); if (newAt != null) { - clientTokenContextManager.removeClientTokenContext(mc); - ClientTokenContext newCtx = initializeClientTokenContext(rc, newAt, ctx.getState()); - clientTokenContextManager.setClientTokenContext(mc, newCtx); - ctx = newCtx; + ((ClientTokenContextImpl)ctx).setToken(newAt); + clientTokenContextManager.setClientTokenContext(mc, ctx); } } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientTokenContextManager.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientTokenContextManager.java index da85e11e9d3..5ffb810310b 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientTokenContextManager.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/MemoryClientTokenContextManager.java @@ -29,9 +29,12 @@ public class MemoryClientTokenContextManager implements ClientTokenContextManage @Override public void setClientTokenContext(MessageContext mc, ClientTokenContext request) { - String stateParam = OAuthUtils.generateRandomTokenKey(); - OAuthUtils.setSessionToken(mc, stateParam, "org.apache.cxf.websso.context", 0); - map.put(stateParam, request); + String key = getKey(mc, false); + if (key == null) { + key = OAuthUtils.generateRandomTokenKey(); + OAuthUtils.setSessionToken(mc, key, "org.apache.cxf.websso.context", 0); + } + map.put(key, request); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java new file mode 100644 index 00000000000..1b8aa72a895 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java @@ -0,0 +1,71 @@ +/** + * 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.client; + +import javax.ws.rs.NotAuthorizedException; + +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.jaxrs.JAXRSInvoker; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.cxf.jaxrs.ext.MessageContextImpl; +import org.apache.cxf.message.Exchange; +import org.apache.cxf.message.Message; +import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; + +public class OAuthInvoker extends JAXRSInvoker { + private WebClient accessTokenServiceClient; + private Consumer consumer; + @Override + public Object invoke(Exchange exchange, Object requestParams, Object resourceObject) { + try { + return super.invoke(exchange, requestParams, resourceObject); + } catch (Fault ex) { + if (ex.getCause() instanceof NotAuthorizedException) { + Message inMessage = exchange.getInMessage(); + ClientTokenContext tokenContext = inMessage.getContent(ClientTokenContext.class); + ClientAccessToken accessToken = tokenContext.getToken(); + String refreshToken = accessToken.getRefreshToken(); + if (refreshToken != null) { + accessToken = OAuthClientUtils.refreshAccessToken(accessTokenServiceClient, + consumer, + accessToken); + ClientTokenContextManager contextManager = + exchange.getInMessage().getContent(ClientTokenContextManager.class); + MessageContext mc = new MessageContextImpl(inMessage); + ((ClientTokenContextImpl)tokenContext).setToken(accessToken); + contextManager.setClientTokenContext(mc, tokenContext); + + //retry + return super.invoke(exchange, requestParams, resourceObject); + } + } + throw ex; + } + } + + public void setAccessTokenServiceClient(WebClient accessTokenServiceClient) { + this.accessTokenServiceClient = accessTokenServiceClient; + } + + + public void setConsumer(Consumer consumer) { + this.consumer = consumer; + } +} From 19df848c9bd3dd84f10c07c384e3fe173921996c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 23 Nov 2015 12:34:17 +0000 Subject: [PATCH 0113/1346] [CXF-6690] Splitting complex XJC parameters --- .../tools/wadlto/jaxb/CustomizationParser.java | 16 ++++++++++++++-- .../cxf/tools/wadlto/jaxrs/WADLToJavaTest.java | 2 ++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxb/CustomizationParser.java b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxb/CustomizationParser.java index 22bd3e4d2ee..80ef483e632 100644 --- a/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxb/CustomizationParser.java +++ b/tools/wadlto/jaxrs/src/main/java/org/apache/cxf/tools/wadlto/jaxb/CustomizationParser.java @@ -22,7 +22,7 @@ import java.io.FileOutputStream; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; @@ -30,6 +30,7 @@ import javax.xml.stream.XMLStreamReader; import org.w3c.dom.Element; + import org.xml.sax.InputSource; import org.apache.cxf.Bus; @@ -90,9 +91,20 @@ public void parse(ToolContext env) { //pass additional JAXB compiler arguments Object jaxbCompilerArgs = env.get(WadlToolConstants.CFG_XJC_ARGS); if (jaxbCompilerArgs != null) { + String[] jaxbArgs = jaxbCompilerArgs instanceof String ? new String[]{(String)jaxbCompilerArgs} : (String[])jaxbCompilerArgs; - compilerArgs.addAll(Arrays.asList(jaxbArgs)); + List jaxbArgsList = new LinkedList(); + for (String jaxbArg : jaxbArgs) { + String[] allArgs = jaxbArg.split(" "); + for (String arg : allArgs) { + String s = arg.trim(); + if (!StringUtils.isEmpty(s)) { + jaxbArgsList.add(s); + } + } + } + compilerArgs.addAll(jaxbArgsList); } // Schema Namespace to Package customizations diff --git a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/WADLToJavaTest.java b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/WADLToJavaTest.java index 0d51674ee11..04dffd7681c 100644 --- a/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/WADLToJavaTest.java +++ b/tools/wadlto/jaxrs/src/test/java/org/apache/cxf/tools/wadlto/jaxrs/WADLToJavaTest.java @@ -74,6 +74,7 @@ public void testGenerateJAXBToString() throws Exception { "custom.service", "-async getName,delete", "-compile", + "-xjc-episode " + output.getAbsolutePath() + "/test.episode", "-xjc-XtoString", getLocation("/wadl/bookstore.xml"), }; @@ -84,6 +85,7 @@ public void testGenerateJAXBToString() throws Exception { verifyFiles("java", true, false, "superbooks", "custom.service"); verifyFiles("class", true, false, "superbooks", "custom.service"); + assertTrue(new File(output.getAbsolutePath() + "/test.episode").exists()); List> schemaClassFiles = getSchemaClassFiles(); assertEquals(4, schemaClassFiles.size()); From 96d0e7c75f6e583f7a3a8ae1849528863c81d5ad Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 23 Nov 2015 14:15:59 +0000 Subject: [PATCH 0114/1346] Setting an implicit flow flag so that the consent form can block a refresh token question --- .../oauth2/common/OAuthAuthorizationData.java | 9 +++++++++ .../oauth2/services/ImplicitGrantService.java | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthAuthorizationData.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthAuthorizationData.java index 7f26bf4d228..05dc72c0dbd 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthAuthorizationData.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/OAuthAuthorizationData.java @@ -46,6 +46,7 @@ public class OAuthAuthorizationData extends OAuthRedirectionState implements Ser private String applicationLogoUri; private List applicationCertificates = new LinkedList(); private Map extraApplicationProperties = new HashMap(); + private boolean implicitFlow; private List permissions; @@ -192,4 +193,12 @@ public List getApplicationCertificates() { public void setApplicationCertificates(List applicationCertificates) { this.applicationCertificates = applicationCertificates; } + + public boolean isImplicitFlow() { + return implicitFlow; + } + + public void setImplicitFlow(boolean implicitFlow) { + this.implicitFlow = implicitFlow; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java index d2dcdbf1d35..057c59d2499 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java @@ -19,10 +19,16 @@ package org.apache.cxf.rs.security.oauth2.services; +import java.util.List; import java.util.Set; import javax.ws.rs.Path; +import javax.ws.rs.core.MultivaluedMap; +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.common.OAuthAuthorizationData; +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.utils.OAuthConstants; @@ -44,6 +50,18 @@ public ImplicitGrantService() { public ImplicitGrantService(Set responseTypes) { super(responseTypes, OAuthConstants.IMPLICIT_GRANT); } + @Override + protected OAuthAuthorizationData createAuthorizationData(Client client, + MultivaluedMap params, + String redirectUri, + UserSubject subject, + List perms, + boolean authorizationCanBeSkipped) { + OAuthAuthorizationData data = + super.createAuthorizationData(client, params, redirectUri, subject, perms, authorizationCanBeSkipped); + data.setImplicitFlow(true); + return data; + } } From 7691c351318c8f600b5b0ff08c4369e3dc86c960 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 23 Nov 2015 17:23:54 +0000 Subject: [PATCH 0115/1346] Spelling correction --- .../apache/cxf/rs/security/oauth2/common/UserSubject.java | 5 +++++ .../org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java index 697222078ba..55f575b7208 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java @@ -134,7 +134,12 @@ public AuthenticationMethod getAuthenticationMethod() { return am; } + @Deprecated public void setAthenticationMethod(AuthenticationMethod method) { + setAuthenticationMethod(method); + } + + public void setAuthenticationMethod(AuthenticationMethod method) { this.am = method; } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java index 51a67a2e9fd..74270a377b9 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java @@ -121,7 +121,7 @@ public static UserSubject createSubject(SecurityContext securityContext) { UserSubject subject = new UserSubject(securityContext.getUserPrincipal().getName(), roleNames); Message m = JAXRSUtils.getCurrentMessage(); if (m != null && m.get(AuthenticationMethod.class) != null) { - subject.setAthenticationMethod(m.get(AuthenticationMethod.class)); + subject.setAuthenticationMethod(m.get(AuthenticationMethod.class)); } return subject; } From c0077dde9d9bab9987bf052b6641df9654b36402 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 23 Nov 2015 17:33:09 +0000 Subject: [PATCH 0116/1346] Recording .gitmergeinfo Changes --- .gitmergeinfo | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmergeinfo b/.gitmergeinfo index b590b1720bc..e195f7ccb70 100644 --- a/.gitmergeinfo +++ b/.gitmergeinfo @@ -6,6 +6,7 @@ B 65e1e07fdb810ec9de135530ca3e3d23821836a3 B 7b7629682d15345518e66d46e575bf1ac334cf00 B 7fc957efa3a193a5f2ae178b8a608717ce4c5b26 B a261507ebd3104b1a00298801ec9815ed1e7a728 +B a7362dfaf2141cb4f303f81bbb94c6df81be75cb B ced98c6e937bd93f92dac9043fa0406c696bfd84 B f0e08b7bea2660542e18294d490e68c7b14aaa4b B f1b56150d6520e73d2ade2296c3b2f13839e63e5 From 4e19e6e11f58a84d875ab96d0df198568858ac89 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 24 Nov 2015 10:47:50 +0000 Subject: [PATCH 0117/1346] Removing deprecated method --- .../apache/cxf/rs/security/oauth2/common/UserSubject.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java index 55f575b7208..b3990c3751f 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/UserSubject.java @@ -134,11 +134,6 @@ public AuthenticationMethod getAuthenticationMethod() { return am; } - @Deprecated - public void setAthenticationMethod(AuthenticationMethod method) { - setAuthenticationMethod(method); - } - public void setAuthenticationMethod(AuthenticationMethod method) { this.am = method; } From 6cb69808178c00e9c935372cd5cf107af8e480e1 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 24 Nov 2015 11:26:09 +0000 Subject: [PATCH 0118/1346] Keeping a single source of signing properties in JwsCompact as discussed with Colm --- .../security/jose/jws/JwsCompactProducer.java | 14 ++------- .../cxf/rs/security/jose/jws/JwsHeaders.java | 9 +++++- .../cxf/rs/security/jose/jws/JwsUtils.java | 7 ++--- .../token/provider/jwt/JWTTokenProvider.java | 30 +++++++------------ 4 files changed, 24 insertions(+), 36 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java index 5ef150a6ffc..53c1b0fe9f4 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java @@ -35,7 +35,6 @@ public class JwsCompactProducer { private String plainJwsPayload; private String signature; private boolean detached; - private Properties signatureProperties; public JwsCompactProducer(String plainJwsPayload) { this(plainJwsPayload, false); } @@ -138,7 +137,7 @@ private SignatureAlgorithm getAlgorithm() { } private void checkAlgorithm() { if (getAlgorithm() == null) { - Properties sigProps = getSignatureProperties(); + Properties sigProps = JwsUtils.loadSignatureOutProperties(false); Message m = PhaseInterceptorChain.getCurrentMessage(); SignatureAlgorithm signatureAlgo = JwsUtils.getSignatureAlgorithm(m, sigProps, null, null); if (signatureAlgo != null) { @@ -150,14 +149,5 @@ private void checkAlgorithm() { throw new JwsException(JwsException.Error.INVALID_ALGORITHM); } } - public Properties getSignatureProperties() { - if (signatureProperties == null) { - signatureProperties = JwsUtils.loadSignatureOutProperties(false); - - } - return signatureProperties; - } - public void setSignatureProperties(Properties signatureProperties) { - this.signatureProperties = signatureProperties; - } + } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsHeaders.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsHeaders.java index a2d0e8844ab..ec75872ae84 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsHeaders.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsHeaders.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.jose.jws; import java.util.Map; +import java.util.Properties; import org.apache.cxf.rs.security.jose.common.JoseConstants; import org.apache.cxf.rs.security.jose.common.JoseHeaders; @@ -31,7 +32,7 @@ public JwsHeaders() { public JwsHeaders(JoseType type) { super(type); } - public JwsHeaders(JoseHeaders headers) { + public JwsHeaders(JwsHeaders headers) { super(headers.asMap()); } @@ -41,6 +42,9 @@ public JwsHeaders(Map values) { public JwsHeaders(SignatureAlgorithm sigAlgo) { init(sigAlgo); } + public JwsHeaders(Properties sigProps) { + init(getSignatureAlgorithm(sigProps)); + } public JwsHeaders(JoseType type, SignatureAlgorithm sigAlgo) { super(type); init(sigAlgo); @@ -63,4 +67,7 @@ public void setPayloadEncodingStatus(Boolean status) { public Boolean getPayloadEncodingStatus() { return super.getBooleanProperty(JoseConstants.JWS_HEADER_B64_STATUS_HEADER); } + private static SignatureAlgorithm getSignatureAlgorithm(Properties sigProps) { + return JwsUtils.getSignatureAlgorithm(sigProps, null); + } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index db12142ee9c..e20388f0678 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -40,7 +40,6 @@ import org.apache.cxf.message.MessageUtils; import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.common.JoseConstants; -import org.apache.cxf.rs.security.jose.common.JoseHeaders; import org.apache.cxf.rs.security.jose.common.JoseUtils; import org.apache.cxf.rs.security.jose.common.KeyManagementUtils; import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; @@ -278,18 +277,18 @@ public static List loadSignatureVerifiers(String propLoc, } return theVerifiers; } - public static boolean validateCriticalHeaders(JoseHeaders headers) { + public static boolean validateCriticalHeaders(JwsHeaders headers) { //TODO: validate JWS specific constraints return JoseUtils.validateCriticalHeaders(headers); } public static JwsSignatureProvider loadSignatureProvider(Properties props, - JoseHeaders headers) { + JwsHeaders headers) { return loadSignatureProvider(PhaseInterceptorChain.getCurrentMessage(), props, headers, false); } public static JwsSignatureProvider loadSignatureProvider(Message m, Properties props, - JoseHeaders headers, + JwsHeaders headers, boolean ignoreNullProvider) { JwsSignatureProvider theSigProvider = null; diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java index 54a4c4e7d7e..1a73d6c81a6 100644 --- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java @@ -32,8 +32,6 @@ import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.message.Message; -import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.jose.common.JoseConstants; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; @@ -41,11 +39,11 @@ import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; import org.apache.cxf.rs.security.jose.jwe.JweHeaders; import org.apache.cxf.rs.security.jose.jwe.JweUtils; +import org.apache.cxf.rs.security.jose.jws.JwsHeaders; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; 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.jose.jwt.JwtToken; import org.apache.cxf.sts.STSPropertiesMBean; import org.apache.cxf.sts.SignatureProperties; import org.apache.cxf.sts.cache.CacheUtils; @@ -119,11 +117,9 @@ public TokenProviderResponse createToken(TokenProviderParameters tokenParameters JwtClaims claims = jwtClaimsProvider.getJwtClaims(jwtClaimsProviderParameters); try { - JwtToken token = new JwtToken(claims); - - String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties()); + String tokenData = signToken(claims, jwtRealm, tokenParameters.getStsProperties()); if (tokenParameters.isEncryptToken()) { - tokenData = encryptToken(tokenData, token.getJweHeaders(), + tokenData = encryptToken(tokenData, new JweHeaders(), tokenParameters.getStsProperties(), tokenParameters.getEncryptionProperties(), tokenParameters.getKeyRequirements()); @@ -205,13 +201,11 @@ public void setJwtClaimsProvider(JWTClaimsProvider jwtClaimsProvider) { } private String signToken( - JwtToken token, + JwtClaims claims, RealmProperties jwtRealm, STSPropertiesMBean stsProperties ) throws Exception { - Properties signingProperties = new Properties(); - if (signToken) { // Initialise signature objects with defaults of STSPropertiesMBean Crypto signatureCrypto = stsProperties.getSignatureCrypto(); @@ -255,6 +249,8 @@ private String signToken( callbackHandler.handle(cb); String password = cb[0].getPassword(); + Properties signingProperties = new Properties(); + signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, signatureAlgorithm); if (alias != null) { signingProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias); @@ -271,20 +267,16 @@ private String signToken( KeyStore keystore = ((Merlin)signatureCrypto).getKeyStore(); signingProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore); - JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token); - jws.setSignatureProperties(signingProperties); + JwsHeaders jwsHeaders = new JwsHeaders(signingProperties); + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(jwsHeaders, claims); - Message m = PhaseInterceptorChain.getCurrentMessage(); JwsSignatureProvider sigProvider = - JwsUtils.loadSignatureProvider(m, signingProperties, token.getJwsHeaders(), false); - token.getJwsHeaders().setSignatureAlgorithm(sigProvider.getAlgorithm()); + JwsUtils.loadSignatureProvider(signingProperties, jwsHeaders); return jws.signWith(sigProvider); } else { - signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, "none"); - - JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token); - jws.setSignatureProperties(signingProperties); + JwsHeaders jwsHeaders = new JwsHeaders(SignatureAlgorithm.NONE); + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(jwsHeaders, claims); return jws.getSignedEncodedJws(); } From c0fd11fa058c0422d84e67b9e45233010cc55f7f Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 24 Nov 2015 12:18:49 +0000 Subject: [PATCH 0119/1346] Updating OAuthInvoker --- .../cxf/rs/security/oauth2/client/OAuthInvoker.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java index 1b8aa72a895..6775b4b73b6 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java @@ -18,9 +18,11 @@ */ package org.apache.cxf.rs.security.oauth2.client; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + import javax.ws.rs.NotAuthorizedException; -import org.apache.cxf.interceptor.Fault; import org.apache.cxf.jaxrs.JAXRSInvoker; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.ext.MessageContext; @@ -33,10 +35,11 @@ public class OAuthInvoker extends JAXRSInvoker { private WebClient accessTokenServiceClient; private Consumer consumer; @Override - public Object invoke(Exchange exchange, Object requestParams, Object resourceObject) { + protected Object performInvocation(Exchange exchange, final Object serviceObject, Method m, + Object[] paramArray) throws Exception { try { - return super.invoke(exchange, requestParams, resourceObject); - } catch (Fault ex) { + return super.performInvocation(exchange, serviceObject, m, paramArray); + } catch (InvocationTargetException ex) { if (ex.getCause() instanceof NotAuthorizedException) { Message inMessage = exchange.getInMessage(); ClientTokenContext tokenContext = inMessage.getContent(ClientTokenContext.class); @@ -53,7 +56,7 @@ public Object invoke(Exchange exchange, Object requestParams, Object resourceObj contextManager.setClientTokenContext(mc, tokenContext); //retry - return super.invoke(exchange, requestParams, resourceObject); + return super.performInvocation(exchange, serviceObject, m, paramArray); } } throw ex; From 9f808d2f250b0323c99c215b18054ffecbdde813 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 24 Nov 2015 14:16:50 +0000 Subject: [PATCH 0120/1346] More updates to OAuthInvoker --- .../security/oauth2/client/ClientCodeRequestFilter.java | 1 - .../cxf/rs/security/oauth2/client/OAuthInvoker.java | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index daaf1212c52..0aa2347916a 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -195,7 +195,6 @@ protected void processCodeResponse(ContainerRequestContext rc, ClientTokenContext tokenContext = initializeClientTokenContext(rc, at, state); if (at != null && clientTokenContextManager != null) { clientTokenContextManager.setClientTokenContext(mc, tokenContext); - JAXRSUtils.getCurrentMessage().setContent(ClientTokenContextManager.class, clientTokenContextManager); } setClientCodeRequest(tokenContext); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java index 6775b4b73b6..8bd49ab0959 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthInvoker.java @@ -33,6 +33,7 @@ public class OAuthInvoker extends JAXRSInvoker { private WebClient accessTokenServiceClient; + private ClientTokenContextManager clientTokenContextManager; private Consumer consumer; @Override protected Object performInvocation(Exchange exchange, final Object serviceObject, Method m, @@ -49,11 +50,9 @@ protected Object performInvocation(Exchange exchange, final Object serviceObject accessToken = OAuthClientUtils.refreshAccessToken(accessTokenServiceClient, consumer, accessToken); - ClientTokenContextManager contextManager = - exchange.getInMessage().getContent(ClientTokenContextManager.class); MessageContext mc = new MessageContextImpl(inMessage); ((ClientTokenContextImpl)tokenContext).setToken(accessToken); - contextManager.setClientTokenContext(mc, tokenContext); + clientTokenContextManager.setClientTokenContext(mc, tokenContext); //retry return super.performInvocation(exchange, serviceObject, m, paramArray); @@ -71,4 +70,8 @@ public void setAccessTokenServiceClient(WebClient accessTokenServiceClient) { public void setConsumer(Consumer consumer) { this.consumer = consumer; } + + public void setClientTokenContextManager(ClientTokenContextManager clientTokenContextManager) { + this.clientTokenContextManager = clientTokenContextManager; + } } From ede8089e0548bd4dd675414840b644dd99316323 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 24 Nov 2015 15:14:15 +0000 Subject: [PATCH 0121/1346] Making the context manager property name consistent --- .../big_query/src/main/webapp/WEB-INF/applicationContext.xml | 4 ++-- .../cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java | 4 ++-- .../cxf/rs/security/oidc/rp/OidcRpAuthenticationService.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml b/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml index 08e0d2342fe..71717977331 100644 --- a/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml +++ b/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml @@ -72,7 +72,7 @@ This state manager is shared between this filter and the RP endpoint, the RP endpoint sets an OIDC context on it and this filter checks the context is available --> - + @@ -102,7 +102,7 @@ - + diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java index de4cad0ce49..e2534ba9300 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationFilter.java @@ -95,7 +95,7 @@ private MultivaluedMap toRequestState(ContainerRequestContext rc public void setRedirectUri(String redirectUri) { this.redirectUri = redirectUri; } - public void setStateManager(ClientTokenContextManager stateManager) { - this.stateManager = stateManager; + public void setClientTokenContextManager(ClientTokenContextManager manager) { + this.stateManager = manager; } } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationService.java index 7c4e66d740e..93fdd1cbdad 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationService.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcRpAuthenticationService.java @@ -75,8 +75,8 @@ public void setDefaultLocation(String defaultLocation) { this.defaultLocation = defaultLocation; } - public void setStateManager(ClientTokenContextManager stateManager) { - this.stateManager = stateManager; + public void setClientTokenContextManager(ClientTokenContextManager manager) { + this.stateManager = manager; } } From c083d093ceb2c9c8fbcc5a05910b04d221225f5f Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 24 Nov 2015 15:15:21 +0000 Subject: [PATCH 0122/1346] Making the context manager property name consistent --- .../basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml b/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml index 3fa9454a2e4..6113a9be4d3 100644 --- a/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml +++ b/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml @@ -44,7 +44,7 @@ This state manager is shared between this filter and the RP endpoint, the RP endpoint sets an OIDC context on it and this filter checks the context is available --> - + @@ -77,7 +77,7 @@ - + From 1b8ab71e424e893a00bb45eb11862bc47d16dce2 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 24 Nov 2015 15:44:17 +0000 Subject: [PATCH 0123/1346] Getting invalid_client reported --- .../cxf/rs/security/oauth2/services/AbstractTokenService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java index 61e3165ef29..a31fb5db126 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractTokenService.java @@ -110,14 +110,14 @@ protected String retrieveClientId(MultivaluedMap params) { protected Client getAndValidateClientFromIdAndSecret(String clientId, String providedClientSecret) { Client client = getClient(clientId); if (!client.getClientId().equals(clientId)) { - throw ExceptionUtils.toNotAuthorizedException(null, null); + reportInvalidClient(); } if (isValidPublicClient(client, clientId, providedClientSecret)) { return client; } if (!client.isConfidential() || !isConfidenatialClientSecretValid(client, providedClientSecret)) { - throw ExceptionUtils.toNotAuthorizedException(null, null); + reportInvalidClient(); } return client; } From 066333db4adeabbbc44a8033b4ac80a16a23ed78 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 25 Nov 2015 12:58:49 +0000 Subject: [PATCH 0124/1346] Reprsenting PublicKeys loaded from Java KeyStore as JWK, renaming DefaultJwkReaderWriter into JwkReaderWriter --- .../security/jose/common/JoseConstants.java | 6 + .../cxf/rs/security/jose/jwe/JweUtils.java | 26 +++- .../jose/jwk/DefaultJwkReaderWriter.java | 49 ------- .../cxf/rs/security/jose/jwk/JsonWebKeys.java | 13 +- .../rs/security/jose/jwk/JwkReaderWriter.java | 27 +++- .../cxf/rs/security/jose/jwk/JwkUtils.java | 123 ++++++++---------- .../cxf/rs/security/jose/jws/JwsUtils.java | 13 +- .../oidc/rp/AbstractTokenValidator.java | 2 + 8 files changed, 129 insertions(+), 130 deletions(-) delete mode 100644 rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java index d7761b455db..70690696284 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java @@ -131,6 +131,12 @@ public final class JoseConstants { */ public static final String RSSEC_SIGNATURE_ALGORITHM = "rs.security.signature.algorithm"; + /** + * The EC Curve to use with EC keys loaded from Java Key Store. + * JWK EC Keys are expected to use a standard "crv" property instead. + */ + public static final String RSSEC_EC_CURVE = "rs.security.elliptic.curve"; + /** * The OLD signature algorithm identifier. Use RSSEC_SIGNATURE_ALGORITHM instead. */ diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java index 8168184bf77..ba902f53d7e 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java @@ -50,6 +50,7 @@ import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; import org.apache.cxf.rs.security.jose.jwk.JwkUtils; import org.apache.cxf.rs.security.jose.jwk.KeyOperation; import org.apache.cxf.rs.security.jose.jwk.KeyType; @@ -149,7 +150,13 @@ public static KeyEncryptionProvider getKeyEncryptionProvider(JsonWebKey jwk, Key } return keyEncryptionProvider; } - public static KeyEncryptionProvider getPublicKeyEncryptionProvider(PublicKey key, KeyAlgorithm algo) { + public static KeyEncryptionProvider getPublicKeyEncryptionProvider(PublicKey key, + KeyAlgorithm algo) { + return getPublicKeyEncryptionProvider(key, null, algo); + } + public static KeyEncryptionProvider getPublicKeyEncryptionProvider(PublicKey key, + Properties props, + KeyAlgorithm algo) { if (key instanceof RSAPublicKey) { return new RSAKeyEncryptionAlgorithm((RSAPublicKey)key, algo); } else if (key instanceof ECPublicKey) { @@ -158,8 +165,10 @@ public static KeyEncryptionProvider getPublicKeyEncryptionProvider(PublicKey key if (m != null) { ctAlgo = getContentAlgo((String)m.get(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM)); } + String curve = props == null ? JsonWebKey.EC_CURVE_P256 + : props.getProperty(JoseConstants.RSSEC_EC_CURVE, JsonWebKey.EC_CURVE_P256); return new EcdhAesWrapKeyEncryptionAlgorithm((ECPublicKey)key, - JsonWebKey.EC_CURVE_P256, + curve, algo, ctAlgo == null ? ContentAlgorithm.A128GCM : ctAlgo); } @@ -358,6 +367,7 @@ public static JweEncryptionProvider loadEncryptionProvider(Properties props, Jwe } else { keyEncryptionProvider = getPublicKeyEncryptionProvider( KeyManagementUtils.loadPublicKey(m, props), + props, keyAlgo); if (includeCert) { headers.setX509Chain(KeyManagementUtils.loadAndEncodeX509CertificateOrChain(m, props)); @@ -775,5 +785,15 @@ public static void checkEncryptionKeySize(Key key) { throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE); } } - + public static JsonWebKeys loadPublicKeyEncryptionKeys(Message m, Properties props) { + String storeType = props.getProperty(JoseConstants.RSSEC_KEY_STORE_TYPE); + if ("jwk".equals(storeType)) { + return JwkUtils.loadPublicJwkSet(m, props); + } else { + //TODO: consider loading all the public keys in the store + PublicKey key = KeyManagementUtils.loadPublicKey(m, props); + JsonWebKey jwk = JwkUtils.fromPublicKey(key, props, JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM); + return new JsonWebKeys(jwk); + } + } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java deleted file mode 100644 index dec80068457..00000000000 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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.jose.jwk; - -import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; - - - - - -public class DefaultJwkReaderWriter extends JsonMapObjectReaderWriter - implements JwkReaderWriter { - @Override - public String jwkSetToJson(JsonWebKeys jwks) { - return toJson(jwks); - } - @Override - public JsonWebKeys jsonToJwkSet(String jwksJson) { - JsonWebKeys jwks = new JsonWebKeys(); - fromJson(jwks, jwksJson); - return jwks; - } - @Override - public String jwkToJson(JsonWebKey jwk) { - return toJson(jwk); - } - @Override - public JsonWebKey jsonToJwk(String jwkJson) { - JsonWebKey jwk = new JsonWebKey(); - fromJson(jwk, jwkJson); - return jwk; - } -} diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java index 28011b3e530..ce53af83f10 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java @@ -29,6 +29,15 @@ public class JsonWebKeys extends JsonMapObject { public static final String KEYS_PROPERTY = "keys"; + public JsonWebKeys() { + + } + public JsonWebKeys(JsonWebKey key) { + setInitKey(key); + } + private void setInitKey(JsonWebKey key) { + setKey(key); + } public List getKeys() { List list = (List)super.getProperty(KEYS_PROPERTY); if (list != null && !list.isEmpty()) { @@ -48,7 +57,9 @@ public List getKeys() { return null; } } - + public void setKey(JsonWebKey key) { + setKeys(Collections.singletonList(key)); + } public void setKeys(List keys) { super.setProperty(KEYS_PROPERTY, keys); } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java index 679b7aa0d27..bbbaaac69ee 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java @@ -18,10 +18,27 @@ */ package org.apache.cxf.rs.security.jose.jwk; +import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; -public interface JwkReaderWriter { - String jwkToJson(JsonWebKey jwk); - JsonWebKey jsonToJwk(String jwkJson); - String jwkSetToJson(JsonWebKeys jwkSet); - JsonWebKeys jsonToJwkSet(String jwkSetJson); + + + + +public class JwkReaderWriter extends JsonMapObjectReaderWriter { + public String jwkSetToJson(JsonWebKeys jwks) { + return toJson(jwks); + } + public JsonWebKeys jsonToJwkSet(String jwksJson) { + JsonWebKeys jwks = new JsonWebKeys(); + fromJson(jwks, jwksJson); + return jwks; + } + public String jwkToJson(JsonWebKey jwk) { + return toJson(jwk); + } + public JsonWebKey jsonToJwk(String jwkJson) { + JsonWebKey jwk = new JsonWebKey(); + fromJson(jwk, jwkJson); + return jwk; + } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index 3fca28d5014..c0bbcba3f5a 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -115,16 +115,16 @@ public static JsonWebKeys readJwkSet(InputStream is) throws IOException { return readJwkSet(IOUtils.readStringFromStream(is)); } public static JsonWebKey readJwkKey(String jwkJson) { - return new DefaultJwkReaderWriter().jsonToJwk(jwkJson); + return new JwkReaderWriter().jsonToJwk(jwkJson); } public static JsonWebKeys readJwkSet(String jwksJson) { - return new DefaultJwkReaderWriter().jsonToJwkSet(jwksJson); + return new JwkReaderWriter().jsonToJwkSet(jwksJson); } public static String jwkKeyToJson(JsonWebKey jwkKey) { - return new DefaultJwkReaderWriter().jwkToJson(jwkKey); + return new JwkReaderWriter().jwkToJson(jwkKey); } public static String jwkSetToJson(JsonWebKeys jwkSet) { - return new DefaultJwkReaderWriter().jwkSetToJson(jwkSet); + return new JwkReaderWriter().jwkSetToJson(jwkSet); } public static String encodeJwkKey(JsonWebKey jwkKey) { return Base64UrlUtility.encode(jwkKeyToJson(jwkKey)); @@ -139,13 +139,10 @@ public static JsonWebKeys decodeJwkSet(String jwksJson) { return readJwkSet(JoseUtils.decodeToString(jwksJson)); } public static String encryptJwkSet(JsonWebKeys jwkSet, char[] password) { - return encryptJwkSet(jwkSet, password, new DefaultJwkReaderWriter()); + return encryptJwkSet(jwkSet, createDefaultEncryption(password)); } - public static String encryptJwkSet(JsonWebKeys jwkSet, char[] password, JwkReaderWriter writer) { - return encryptJwkSet(jwkSet, createDefaultEncryption(password), writer); - } - public static String encryptJwkSet(JsonWebKeys jwkSet, JweEncryptionProvider jwe, JwkReaderWriter writer) { - return jwe.encrypt(StringUtils.toBytesUTF8(writer.jwkSetToJson(jwkSet)), + public static String encryptJwkSet(JsonWebKeys jwkSet, JweEncryptionProvider jwe) { + return jwe.encrypt(StringUtils.toBytesUTF8(new JwkReaderWriter().jwkSetToJson(jwkSet)), toJweHeaders("jwk-set+json")); } public static String encryptJwkSet(JsonWebKeys jwkSet, PublicKey key, KeyAlgorithm keyAlgo, @@ -162,13 +159,10 @@ public static String encryptJwkSet(JsonWebKeys jwkSet, SecretKey key, KeyAlgorit "jwk-set+json"); } public static JsonWebKeys decryptJwkSet(String jsonJwkSet, char[] password) { - return decryptJwkSet(jsonJwkSet, password, new DefaultJwkReaderWriter()); - } - public static JsonWebKeys decryptJwkSet(String jsonJwkSet, char[] password, JwkReaderWriter reader) { - return decryptJwkSet(jsonJwkSet, createDefaultDecryption(password), reader); + return decryptJwkSet(jsonJwkSet, createDefaultDecryption(password)); } - public static JsonWebKeys decryptJwkSet(String jsonJwkSet, JweDecryptionProvider jwe, JwkReaderWriter reader) { - return reader.jsonToJwkSet(jwe.decrypt(jsonJwkSet).getContentText()); + public static JsonWebKeys decryptJwkSet(String jsonJwkSet, JweDecryptionProvider jwe) { + return new JwkReaderWriter().jsonToJwkSet(jwe.decrypt(jsonJwkSet).getContentText()); } public static JsonWebKeys decryptJwkSet(PrivateKey key, KeyAlgorithm keyAlgo, ContentAlgorithm ctAlgo, String jsonJwkSet) { @@ -181,25 +175,20 @@ public static JsonWebKeys decryptJwkSet(SecretKey key, KeyAlgorithm keyAlgo, Con String jsonJwkSet) { return readJwkSet(toString(JweUtils.decrypt(key, keyAlgo, ctAlgo, jsonJwkSet))); } - public static JsonWebKeys decryptJwkSet(InputStream is, char[] password) throws IOException { - return decryptJwkSet(is, password, new DefaultJwkReaderWriter()); - } - public static JsonWebKeys decryptJwkSet(InputStream is, char[] password, JwkReaderWriter reader) + public static JsonWebKeys decryptJwkSet(InputStream is, char[] password) throws IOException { - return decryptJwkSet(is, createDefaultDecryption(password), reader); + return decryptJwkSet(is, createDefaultDecryption(password)); } - public static JsonWebKeys decryptJwkSet(InputStream is, JweDecryptionProvider jwe, JwkReaderWriter reader) + public static JsonWebKeys decryptJwkSet(InputStream is, JweDecryptionProvider jwe) throws IOException { - return reader.jsonToJwkSet(jwe.decrypt(IOUtils.readStringFromStream(is)).getContentText()); + return new JwkReaderWriter().jsonToJwkSet( + jwe.decrypt(IOUtils.readStringFromStream(is)).getContentText()); } - public static String encryptJwkKey(JsonWebKey jwk, char[] password) { - return encryptJwkKey(jwk, password, new DefaultJwkReaderWriter()); + public static String encryptJwkKey(JsonWebKey jwkKey, char[] password) { + return encryptJwkKey(jwkKey, createDefaultEncryption(password)); } - public static String encryptJwkKey(JsonWebKey jwkKey, char[] password, JwkReaderWriter writer) { - return encryptJwkKey(jwkKey, createDefaultEncryption(password), writer); - } - public static String encryptJwkKey(JsonWebKey jwkKey, JweEncryptionProvider jwe, JwkReaderWriter writer) { - return jwe.encrypt(StringUtils.toBytesUTF8(writer.jwkToJson(jwkKey)), + public static String encryptJwkKey(JsonWebKey jwkKey, JweEncryptionProvider jwe) { + return jwe.encrypt(StringUtils.toBytesUTF8(new JwkReaderWriter().jwkToJson(jwkKey)), toJweHeaders("jwk+json")); } public static String encryptJwkKey(JsonWebKey jwkKey, PublicKey key, KeyAlgorithm keyAlgo, @@ -216,10 +205,7 @@ public static String signJwkKey(JsonWebKey jwkKey, PrivateKey key, SignatureAlgo return JwsUtils.sign(key, algo, jwkKeyToJson(jwkKey), "jwk+json"); } public static JsonWebKey decryptJwkKey(String jsonJwkKey, char[] password) { - return decryptJwkKey(jsonJwkKey, password, new DefaultJwkReaderWriter()); - } - public static JsonWebKey decryptJwkKey(String jsonJwkKey, char[] password, JwkReaderWriter reader) { - return decryptJwkKey(jsonJwkKey, createDefaultDecryption(password), reader); + return decryptJwkKey(jsonJwkKey, createDefaultDecryption(password)); } public static JsonWebKey decryptJwkKey(PrivateKey key, KeyAlgorithm keyAlgo, ContentAlgorithm ctAlgo, String jsonJwk) { @@ -232,29 +218,26 @@ public static JsonWebKey decryptJwkKey(SecretKey key, KeyAlgorithm keyAlgo, Cont String jsonJwk) { return readJwkKey(toString(JweUtils.decrypt(key, keyAlgo, ctAlgo, jsonJwk))); } - public static JsonWebKey decryptJwkKey(String jsonJwkKey, JweDecryptionProvider jwe, JwkReaderWriter reader) { - return reader.jsonToJwk(jwe.decrypt(jsonJwkKey).getContentText()); + public static JsonWebKey decryptJwkKey(String jsonJwkKey, JweDecryptionProvider jwe) { + return new JwkReaderWriter().jsonToJwk(jwe.decrypt(jsonJwkKey).getContentText()); } - public static JsonWebKey decryptJwkKey(InputStream is, char[] password) throws IOException { - return decryptJwkKey(is, password, new DefaultJwkReaderWriter()); - } - public static JsonWebKey decryptJwkKey(InputStream is, char[] password, JwkReaderWriter reader) + public static JsonWebKey decryptJwkKey(InputStream is, char[] password) throws IOException { - return decryptJwkKey(is, createDefaultDecryption(password), reader); + return decryptJwkKey(is, createDefaultDecryption(password)); } - public static JsonWebKey decryptJwkKey(InputStream is, JweDecryptionProvider jwe, JwkReaderWriter reader) + public static JsonWebKey decryptJwkKey(InputStream is, JweDecryptionProvider jwe) throws IOException { - return reader.jsonToJwk(jwe.decrypt(IOUtils.readStringFromStream(is)).getContentText()); + return new JwkReaderWriter().jsonToJwk( + jwe.decrypt(IOUtils.readStringFromStream(is)).getContentText()); } - public static JsonWebKeys loadJwkSet(Message m, Properties props, PrivateKeyPasswordProvider cb) { - return loadJwkSet(m, props, cb, new DefaultJwkReaderWriter()); + public static JsonWebKeys loadPublicJwkSet(Message m, Properties props) { + return loadJwkSet(m, props, null); } - public static JsonWebKeys loadJwkSet(Message m, Properties props, PrivateKeyPasswordProvider cb, - JwkReaderWriter reader) { + public static JsonWebKeys loadJwkSet(Message m, Properties props, PrivateKeyPasswordProvider cb) { String key = (String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE); JsonWebKeys jwkSet = key != null ? (JsonWebKeys)m.getExchange().get(key) : null; if (jwkSet == null) { - jwkSet = loadJwkSet(props, m.getExchange().getBus(), cb, reader); + jwkSet = loadJwkSet(props, m.getExchange().getBus(), cb); if (key != null) { m.getExchange().put(key, jwkSet); } @@ -262,16 +245,12 @@ public static JsonWebKeys loadJwkSet(Message m, Properties props, PrivateKeyPass return jwkSet; } public static JsonWebKeys loadJwkSet(Properties props, Bus bus, PrivateKeyPasswordProvider cb) { - return loadJwkSet(props, bus, cb, new DefaultJwkReaderWriter()); - } - public static JsonWebKeys loadJwkSet(Properties props, Bus bus, PrivateKeyPasswordProvider cb, - JwkReaderWriter reader) { JweDecryptionProvider decryption = cb != null ? new AesCbcHmacJweDecryption(new PbesHmacAesWrapKeyDecryptionAlgorithm( cb.getPassword(props))) : null; - return loadJwkSet(props, bus, decryption, reader); + return loadJwkSet(props, bus, decryption); } - public static JsonWebKeys loadJwkSet(Properties props, Bus bus, JweDecryptionProvider jwe, JwkReaderWriter reader) { + public static JsonWebKeys loadJwkSet(Properties props, Bus bus, JweDecryptionProvider jwe) { String keyContent = null; String keyStoreLoc = props.getProperty(JoseConstants.RSSEC_KEY_STORE_FILE); if (keyStoreLoc != null) { @@ -293,25 +272,21 @@ public static JsonWebKeys loadJwkSet(Properties props, Bus bus, JweDecryptionPro if (jwe != null) { keyContent = jwe.decrypt(keyContent).getContentText(); } + JwkReaderWriter reader = new JwkReaderWriter(); if (props.getProperty(JoseConstants.RSSEC_KEY_STORE_JWKKEY) == null) { return reader.jsonToJwkSet(keyContent); } else { - JsonWebKey key = reader.jsonToJwk(keyContent); - JsonWebKeys keys = new JsonWebKeys(); - keys.setKeys(Collections.singletonList(key)); - return keys; + JsonWebKey jwk = reader.jsonToJwk(keyContent); + return new JsonWebKeys(jwk); } } + public static JsonWebKey loadJsonWebKey(Message m, Properties props, KeyOperation keyOper) { return loadJsonWebKey(m, props, keyOper, null); } public static JsonWebKey loadJsonWebKey(Message m, Properties props, KeyOperation keyOper, String inHeaderKid) { - return loadJsonWebKey(m, props, keyOper, inHeaderKid, new DefaultJwkReaderWriter()); - } - public static JsonWebKey loadJsonWebKey(Message m, Properties props, KeyOperation keyOper, String inHeaderKid, - JwkReaderWriter reader) { PrivateKeyPasswordProvider cb = KeyManagementUtils.loadPasswordProvider(m, props, keyOper); - JsonWebKeys jwkSet = loadJwkSet(m, props, cb, reader); + JsonWebKeys jwkSet = loadJwkSet(m, props, cb); String kid = null; if (inHeaderKid != null && MessageUtils.getContextualBoolean(m, JoseConstants.RSSEC_ACCEPT_PUBLIC_KEY, false)) { @@ -329,15 +304,11 @@ public static JsonWebKey loadJsonWebKey(Message m, Properties props, KeyOperatio } return null; } - public static List loadJsonWebKeys(Message m, Properties props, KeyOperation keyOper) { - return loadJsonWebKeys(m, props, keyOper, new DefaultJwkReaderWriter()); - } - - public static List loadJsonWebKeys(Message m, Properties props, - KeyOperation keyOper, - JwkReaderWriter reader) { + public static List loadJsonWebKeys(Message m, + Properties props, + KeyOperation keyOper) { PrivateKeyPasswordProvider cb = KeyManagementUtils.loadPasswordProvider(m, props, keyOper); - JsonWebKeys jwkSet = loadJwkSet(m, props, cb, reader); + JsonWebKeys jwkSet = loadJwkSet(m, props, cb); String kid = KeyManagementUtils.getKeyId(m, props, JoseConstants.RSSEC_KEY_STORE_ALIAS, keyOper); if (kid != null) { return Collections.singletonList(jwkSet.getKey(kid)); @@ -401,6 +372,16 @@ public static JsonWebKey fromRSAPublicKey(RSAPublicKey pk, String algo) { jwk.setProperty(JsonWebKey.RSA_PUBLIC_EXP, encodedPublicExponent); return jwk; } + public static JsonWebKey fromPublicKey(PublicKey key, Properties props, String algoProp) { + // EC keys can be supported once we figure out how to get a curve name + // from an EC key instance or if a curve property is introduced + if (key instanceof RSAPublicKey) { + return JwkUtils.fromRSAPublicKey((RSAPublicKey)key, algoProp); + } else { + return JwkUtils.fromECPublicKey((ECPublicKey)key, + props.getProperty(JoseConstants.RSSEC_EC_CURVE)); + } + } public static JsonWebKey fromX509CertificateChain(List chain, String algo) { JsonWebKey jwk = new JsonWebKey(); jwk.setAlgorithm(algo); diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index e20388f0678..710baa7bef9 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -45,6 +45,7 @@ import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; import org.apache.cxf.rs.security.jose.jwk.JwkUtils; import org.apache.cxf.rs.security.jose.jwk.KeyOperation; import org.apache.cxf.rs.security.jose.jwk.KeyType; @@ -503,5 +504,15 @@ public static void checkSignatureKeySize(Key key) { throw new JwsException(JwsException.Error.INVALID_KEY); } } - + public static JsonWebKeys loadPublicVerificationKeys(Message m, Properties props) { + String storeType = props.getProperty(JoseConstants.RSSEC_KEY_STORE_TYPE); + if ("jwk".equals(storeType)) { + return JwkUtils.loadPublicJwkSet(m, props); + } else { + //TODO: consider loading all the public keys in the store + PublicKey key = KeyManagementUtils.loadPublicKey(m, props); + JsonWebKey jwk = JwkUtils.fromPublicKey(key, props, JoseConstants.RSSEC_SIGNATURE_ALGORITHM); + return new JsonWebKeys(jwk); + } + } } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java index 6ee14ac5e19..60115771a85 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java @@ -122,6 +122,8 @@ protected JwsSignatureVerifier getInitializedSignatureVerifier(JwtToken jwt) { } else if (keys.getKeys().size() == 1) { key = keys.getKeys().get(0); } + //jwkSetClient returns the most up-to-date keys + keyMap.clear(); keyMap.putAll(keys.getKeyIdMap()); } } From 628e0418db207fce80c7782d8e56f8d3a5d7c6ab Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 25 Nov 2015 13:01:48 +0000 Subject: [PATCH 0125/1346] Removing redundant comment --- .../main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index c0bbcba3f5a..94e7884b3fd 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -373,8 +373,6 @@ public static JsonWebKey fromRSAPublicKey(RSAPublicKey pk, String algo) { return jwk; } public static JsonWebKey fromPublicKey(PublicKey key, Properties props, String algoProp) { - // EC keys can be supported once we figure out how to get a curve name - // from an EC key instance or if a curve property is introduced if (key instanceof RSAPublicKey) { return JwkUtils.fromRSAPublicKey((RSAPublicKey)key, algoProp); } else { From a837a8fa567526c3c674d6b6b9a53fd01d4eef95 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 25 Nov 2015 13:16:17 +0000 Subject: [PATCH 0126/1346] Setting kid too --- .../org/apache/cxf/rs/security/jose/jwk/JwkUtils.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index 94e7884b3fd..eca04a514a1 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -373,12 +373,18 @@ public static JsonWebKey fromRSAPublicKey(RSAPublicKey pk, String algo) { return jwk; } public static JsonWebKey fromPublicKey(PublicKey key, Properties props, String algoProp) { + JsonWebKey jwk = null; if (key instanceof RSAPublicKey) { - return JwkUtils.fromRSAPublicKey((RSAPublicKey)key, algoProp); + jwk = JwkUtils.fromRSAPublicKey((RSAPublicKey)key, props.getProperty(algoProp)); } else { - return JwkUtils.fromECPublicKey((ECPublicKey)key, + jwk = JwkUtils.fromECPublicKey((ECPublicKey)key, props.getProperty(JoseConstants.RSSEC_EC_CURVE)); } + String kid = props.getProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS); + if (kid != null) { + jwk.setKeyId(kid); + } + return jwk; } public static JsonWebKey fromX509CertificateChain(List chain, String algo) { JsonWebKey jwk = new JsonWebKey(); From deea740143ef9b2d85c5dc2139f6918ae10ca548 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 25 Nov 2015 14:01:51 +0000 Subject: [PATCH 0127/1346] Prototyping OIDC Keys service --- .../rs/security/oidc/idp/OidcKeysService.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java new file mode 100644 index 00000000000..f6c214f773a --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java @@ -0,0 +1,46 @@ +/** + * 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.oidc.idp; + +import java.util.Properties; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; + +@Path("keys") +public class OidcKeysService { + + private volatile JsonWebKeys keySet; + + @GET + @Produces("application/json") + public JsonWebKeys getPublicVerificationKeys() { + Properties props = JwsUtils.loadSignatureInProperties(true); + if (keySet == null) { + keySet = JwsUtils.loadPublicVerificationKeys(JAXRSUtils.getCurrentMessage(), props); + } + return keySet; + } + +} From ea2453e13f37be5e86fa4c07c110a69a32e47aae Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 25 Nov 2015 16:07:45 +0000 Subject: [PATCH 0128/1346] Fixed a bug with the expiry of refresh tokens with the EhCache provider --- .../rs/security/oauth2/provider/AbstractOAuthDataProvider.java | 2 +- .../org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 8eba9365d14..d40d6684411 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 @@ -32,7 +32,7 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider { private long accessTokenLifetime = 3600L; - private long refreshTokenLifetime = -1; + private long refreshTokenLifetime; // refresh tokens are eternal by default protected AbstractOAuthDataProvider() { } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java index 74270a377b9..7722d7ac147 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthUtils.java @@ -183,7 +183,7 @@ public static long getIssuedAt() { } public static boolean isExpired(Long issuedAt, Long lifetime) { - return lifetime != -1 + return lifetime != 0L && issuedAt + lifetime < System.currentTimeMillis() / 1000; } From cf25923774acf5cabb2775da729f9f51d8347e5d Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 26 Nov 2015 09:42:29 +0000 Subject: [PATCH 0129/1346] Checking if the refresh token has expired and minor changes to JwkUtils --- .../rs/security/jose/jwa/AlgorithmUtils.java | 3 ++ .../cxf/rs/security/jose/jwk/JwkUtils.java | 31 +++++++++++-------- .../provider/AbstractOAuthDataProvider.java | 4 ++- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java index 0145b5d028f..d52054b4f8e 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwa/AlgorithmUtils.java @@ -197,6 +197,9 @@ private AlgorithmUtils() { public static boolean isRsa(String algo) { return isRsaKeyWrap(algo) || isRsaSign(algo); } + public static boolean isEc(String algo) { + return isEcDsaSign(algo) || isEcdhEsWrap(algo); + } public static boolean isRsaKeyWrap(String algo) { return RSA_CEK_SET.contains(algo); } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index eca04a514a1..38c299a1bc7 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -349,9 +349,7 @@ public static List toX509CertificateChain(JsonWebKey jwk) { return KeyManagementUtils.toX509CertificateChain(base64EncodedChain); } public static JsonWebKey fromECPublicKey(ECPublicKey pk, String curve) { - JsonWebKey jwk = new JsonWebKey(); - jwk.setKeyType(KeyType.EC); - jwk.setProperty(JsonWebKey.EC_CURVE, curve); + JsonWebKey jwk = prepareECJwk(curve); jwk.setProperty(JsonWebKey.EC_X_COORDINATE, Base64UrlUtility.encode(pk.getW().getAffineX().toByteArray())); jwk.setProperty(JsonWebKey.EC_Y_COORDINATE, @@ -359,9 +357,7 @@ public static JsonWebKey fromECPublicKey(ECPublicKey pk, String curve) { return jwk; } public static JsonWebKey fromECPrivateKey(ECPrivateKey pk, String curve) { - JsonWebKey jwk = new JsonWebKey(); - jwk.setKeyType(KeyType.EC); - jwk.setProperty(JsonWebKey.EC_CURVE, curve); + JsonWebKey jwk = prepareECJwk(curve); jwk.setProperty(JsonWebKey.EC_PRIVATE_KEY, Base64UrlUtility.encode(pk.getS().toByteArray())); return jwk; @@ -375,10 +371,11 @@ public static JsonWebKey fromRSAPublicKey(RSAPublicKey pk, String algo) { public static JsonWebKey fromPublicKey(PublicKey key, Properties props, String algoProp) { JsonWebKey jwk = null; if (key instanceof RSAPublicKey) { - jwk = JwkUtils.fromRSAPublicKey((RSAPublicKey)key, props.getProperty(algoProp)); + String algo = props.getProperty(algoProp); + jwk = JwkUtils.fromRSAPublicKey((RSAPublicKey)key, algo); } else { - jwk = JwkUtils.fromECPublicKey((ECPublicKey)key, - props.getProperty(JoseConstants.RSSEC_EC_CURVE)); + jwk = JwkUtils.fromECPublicKey((ECPublicKey)key, + props.getProperty(JoseConstants.RSSEC_EC_CURVE)); } String kid = props.getProperty(JoseConstants.RSSEC_KEY_STORE_ALIAS); if (kid != null) { @@ -475,16 +472,24 @@ private static JweDecryptionProvider createDefaultDecryption(char[] password) { return new AesCbcHmacJweDecryption(keyDecryption); } private static JsonWebKey prepareRSAJwk(BigInteger modulus, String algo) { - if (!AlgorithmUtils.isRsa(algo)) { - throw new JwkException("Invalid algorithm"); - } JsonWebKey jwk = new JsonWebKey(); jwk.setKeyType(KeyType.RSA); - jwk.setAlgorithm(algo); + if (algo != null) { + if (!AlgorithmUtils.isRsa(algo)) { + throw new JwkException("Invalid algorithm"); + } + jwk.setAlgorithm(algo); + } String encodedModulus = Base64UrlUtility.encode(modulus.toByteArray()); jwk.setProperty(JsonWebKey.RSA_MODULUS, encodedModulus); return jwk; } + private static JsonWebKey prepareECJwk(String curve) { + JsonWebKey jwk = new JsonWebKey(); + jwk.setKeyType(KeyType.EC); + jwk.setProperty(JsonWebKey.EC_CURVE, curve); + return jwk; + } private static String toString(byte[] bytes) { try { return new String(bytes, StandardCharsets.UTF_8); 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 d40d6684411..0346c090c8f 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 @@ -29,6 +29,7 @@ import org.apache.cxf.rs.security.oauth2.tokens.bearer.BearerAccessToken; import org.apache.cxf.rs.security.oauth2.tokens.refresh.RefreshToken; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; public abstract class AbstractOAuthDataProvider implements OAuthDataProvider { private long accessTokenLifetime = 3600L; @@ -52,7 +53,8 @@ public void removeAccessToken(ServerAccessToken token) throws OAuthServiceExcept public ServerAccessToken refreshAccessToken(Client client, String refreshTokenKey, List restrictedScopes) throws OAuthServiceException { RefreshToken oldRefreshToken = revokeRefreshAndAccessTokens(client, refreshTokenKey); - if (oldRefreshToken == null) { + if (oldRefreshToken == null + || OAuthUtils.isExpired(oldRefreshToken.getIssuedAt(), oldRefreshToken.getExpiresIn())) { throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED); } return doRefreshAccessToken(client, oldRefreshToken, restrictedScopes); From d5bfcb30520bc8422b3aae0335c57acd6f51d3a2 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 26 Nov 2015 10:17:56 +0000 Subject: [PATCH 0130/1346] Updating DefaultEHCacheCodeDataProvider codeLifetime to the max recommended value --- .../oauth2/grants/code/DefaultEHCacheCodeDataProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java index ea9ace83885..14193293493 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java @@ -28,7 +28,7 @@ public class DefaultEHCacheCodeDataProvider extends DefaultEHCacheOAuthDataProvi implements AuthorizationCodeDataProvider { public static final String CODE_GRANT_CACHE_KEY = "cxf.oauth2.codegrant.cache"; - private long codeLifetime = 3600L; + private long codeLifetime = 10 * 60; private Ehcache codeGrantCache; protected DefaultEHCacheCodeDataProvider() { From fe8951e0cbbff569db9cedc53e4b5a18ef8c9cb3 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 26 Nov 2015 11:51:14 +0000 Subject: [PATCH 0131/1346] Updating the oidc pom to include jose-jaxrs --- rt/rs/security/sso/oidc/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rt/rs/security/sso/oidc/pom.xml b/rt/rs/security/sso/oidc/pom.xml index e02160b2d55..e9b63c3e772 100644 --- a/rt/rs/security/sso/oidc/pom.xml +++ b/rt/rs/security/sso/oidc/pom.xml @@ -36,6 +36,11 @@ cxf-rt-rs-security-oauth2 ${project.version} + + org.apache.cxf + cxf-rt-rs-security-jose-jaxrs + ${project.version} + junit From ac40bcfa82036c262aa1cf21bcf1d4d91185b51c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 26 Nov 2015 12:46:08 +0000 Subject: [PATCH 0132/1346] Updating JsonWebKeysProvider to support MBW --- .../jose/jaxrs/JsonWebKeysProvider.java | 24 ++++++++++++++++++- .../cxf/rs/security/jose/jwk/JwkUtils.java | 8 +++++++ .../rs/security/oidc/idp/OidcKeysService.java | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JsonWebKeysProvider.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JsonWebKeysProvider.java index a877925b933..7a4adc773cc 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JsonWebKeysProvider.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JsonWebKeysProvider.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; @@ -27,11 +28,12 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; import org.apache.cxf.rs.security.jose.jwk.JwkUtils; -public class JsonWebKeysProvider implements MessageBodyReader { +public class JsonWebKeysProvider implements MessageBodyReader, MessageBodyWriter { @Override public boolean isReadable(Class cls, Type type, Annotation[] anns, MediaType mt) { @@ -44,5 +46,25 @@ public JsonWebKeys readFrom(Class cls, Type t, Annotation[] anns, M WebApplicationException { return JwkUtils.readJwkSet(is); } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, + MediaType mediaType) { + return type == JsonWebKeys.class; + } + + @Override + public long getSize(JsonWebKeys t, Class type, Type genericType, Annotation[] annotations, + MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(JsonWebKeys t, Class type, Type genericType, Annotation[] annotations, + MediaType mediaType, MultivaluedMap httpHeaders, + OutputStream entityStream) throws IOException, WebApplicationException { + JwkUtils.jwkSetToJson(t, entityStream); + + } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java index 38c299a1bc7..e5c93c562cb 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -18,8 +18,10 @@ */ package org.apache.cxf.rs.security.jose.jwk; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.math.BigInteger; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -123,9 +125,15 @@ public static JsonWebKeys readJwkSet(String jwksJson) { public static String jwkKeyToJson(JsonWebKey jwkKey) { return new JwkReaderWriter().jwkToJson(jwkKey); } + public static void jwkKeyToJson(JsonWebKey jwkKey, OutputStream os) throws IOException { + IOUtils.copy(new ByteArrayInputStream(StringUtils.toBytesUTF8(jwkKeyToJson(jwkKey))), os); + } public static String jwkSetToJson(JsonWebKeys jwkSet) { return new JwkReaderWriter().jwkSetToJson(jwkSet); } + public static void jwkSetToJson(JsonWebKeys jwkSet, OutputStream os) throws IOException { + IOUtils.copy(new ByteArrayInputStream(StringUtils.toBytesUTF8(jwkSetToJson(jwkSet))), os); + } public static String encodeJwkKey(JsonWebKey jwkKey) { return Base64UrlUtility.encode(jwkKeyToJson(jwkKey)); } diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java index f6c214f773a..e2c140d41a1 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/OidcKeysService.java @@ -36,8 +36,8 @@ public class OidcKeysService { @GET @Produces("application/json") public JsonWebKeys getPublicVerificationKeys() { - Properties props = JwsUtils.loadSignatureInProperties(true); if (keySet == null) { + Properties props = JwsUtils.loadSignatureInProperties(true); keySet = JwsUtils.loadPublicVerificationKeys(JAXRSUtils.getCurrentMessage(), props); } return keySet; From 365cbd07b67cdae4dd29089c1d9bd04fe6a76ed8 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 26 Nov 2015 14:53:53 +0000 Subject: [PATCH 0133/1346] Adding more SAML/JMS tests --- .../jms/security/JMSWSSecurityTest.java | 122 +++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java b/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java index fc8ffb2c46b..cfaf7110ac8 100644 --- a/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java +++ b/systests/transport-jms/src/test/java/org/apache/cxf/systest/jms/security/JMSWSSecurityTest.java @@ -26,6 +26,7 @@ import java.util.Map; import javax.xml.namespace.QName; +import javax.xml.ws.soap.SOAPFaultException; import org.apache.cxf.BusFactory; import org.apache.cxf.endpoint.Client; @@ -112,7 +113,7 @@ public void testUnsignedSAML2Token() throws Exception { } @Test - public void testUnsignedSAML2AudienceRestrictionToken() throws Exception { + public void testUnsignedSAML2AudienceRestrictionTokenURI() throws Exception { QName serviceName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldService"); QName portName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldPort"); URL wsdl = getWSDLURL("/wsdl/jms_test.wsdl"); @@ -150,5 +151,124 @@ public void testUnsignedSAML2AudienceRestrictionToken() throws Exception { ((java.io.Closeable)greeter).close(); } + @Test + public void testUnsignedSAML2AudienceRestrictionTokenBadURI() throws Exception { + QName serviceName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldService"); + QName portName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldPort"); + URL wsdl = getWSDLURL("/wsdl/jms_test.wsdl"); + HelloWorldService service = new HelloWorldService(wsdl, serviceName); + + HelloWorldPortType greeter = service.getPort(portName, HelloWorldPortType.class); + + SamlCallbackHandler callbackHandler = new SamlCallbackHandler(); + callbackHandler.setSignAssertion(true); + callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER); + + ConditionsBean conditions = new ConditionsBean(); + conditions.setTokenPeriodMinutes(5); + List audiences = new ArrayList<>(); + audiences.add("jms:jndi:dynamicQueues/test.jmstransport.text.bad"); + AudienceRestrictionBean audienceRestrictionBean = new AudienceRestrictionBean(); + audienceRestrictionBean.setAudienceURIs(audiences); + conditions.setAudienceRestrictions(Collections.singletonList(audienceRestrictionBean)); + + callbackHandler.setConditions(conditions); + + Map outProperties = new HashMap(); + outProperties.put(WSHandlerConstants.ACTION, WSHandlerConstants.SAML_TOKEN_UNSIGNED); + outProperties.put(WSHandlerConstants.SAML_CALLBACK_REF, callbackHandler); + + WSS4JOutInterceptor outInterceptor = new WSS4JOutInterceptor(outProperties); + Client client = ClientProxy.getClient(greeter); + client.getOutInterceptors().add(outInterceptor); + + try { + greeter.sayHi(); + fail("Failure expected on a bad audience restriction"); + } catch (SOAPFaultException ex) { + // expected + } + + ((java.io.Closeable)greeter).close(); + } + + @Test + public void testUnsignedSAML2AudienceRestrictionTokenServiceName() throws Exception { + QName serviceName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldService"); + QName portName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldPort"); + URL wsdl = getWSDLURL("/wsdl/jms_test.wsdl"); + HelloWorldService service = new HelloWorldService(wsdl, serviceName); + String response = new String("Bonjour"); + HelloWorldPortType greeter = service.getPort(portName, HelloWorldPortType.class); + + SamlCallbackHandler callbackHandler = new SamlCallbackHandler(); + callbackHandler.setSignAssertion(true); + callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER); + + ConditionsBean conditions = new ConditionsBean(); + conditions.setTokenPeriodMinutes(5); + List audiences = new ArrayList<>(); + audiences.add("{http://cxf.apache.org/hello_world_jms}HelloWorldService"); + AudienceRestrictionBean audienceRestrictionBean = new AudienceRestrictionBean(); + audienceRestrictionBean.setAudienceURIs(audiences); + conditions.setAudienceRestrictions(Collections.singletonList(audienceRestrictionBean)); + + callbackHandler.setConditions(conditions); + + Map outProperties = new HashMap(); + outProperties.put(WSHandlerConstants.ACTION, WSHandlerConstants.SAML_TOKEN_UNSIGNED); + outProperties.put(WSHandlerConstants.SAML_CALLBACK_REF, callbackHandler); + + WSS4JOutInterceptor outInterceptor = new WSS4JOutInterceptor(outProperties); + Client client = ClientProxy.getClient(greeter); + client.getOutInterceptors().add(outInterceptor); + + String reply = greeter.sayHi(); + assertNotNull("no response received from service", reply); + assertEquals(response, reply); + + ((java.io.Closeable)greeter).close(); + } + + @Test + public void testUnsignedSAML2AudienceRestrictionTokenBadServiceName() throws Exception { + QName serviceName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldService"); + QName portName = new QName("http://cxf.apache.org/hello_world_jms", "HelloWorldPort"); + URL wsdl = getWSDLURL("/wsdl/jms_test.wsdl"); + HelloWorldService service = new HelloWorldService(wsdl, serviceName); + + HelloWorldPortType greeter = service.getPort(portName, HelloWorldPortType.class); + + SamlCallbackHandler callbackHandler = new SamlCallbackHandler(); + callbackHandler.setSignAssertion(true); + callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER); + + ConditionsBean conditions = new ConditionsBean(); + conditions.setTokenPeriodMinutes(5); + List audiences = new ArrayList<>(); + audiences.add("{http://cxf.apache.org/hello_world_jms}BadHelloWorldService"); + AudienceRestrictionBean audienceRestrictionBean = new AudienceRestrictionBean(); + audienceRestrictionBean.setAudienceURIs(audiences); + conditions.setAudienceRestrictions(Collections.singletonList(audienceRestrictionBean)); + + callbackHandler.setConditions(conditions); + + Map outProperties = new HashMap(); + outProperties.put(WSHandlerConstants.ACTION, WSHandlerConstants.SAML_TOKEN_UNSIGNED); + outProperties.put(WSHandlerConstants.SAML_CALLBACK_REF, callbackHandler); + + WSS4JOutInterceptor outInterceptor = new WSS4JOutInterceptor(outProperties); + Client client = ClientProxy.getClient(greeter); + client.getOutInterceptors().add(outInterceptor); + + try { + greeter.sayHi(); + fail("Failure expected on a bad audience restriction"); + } catch (SOAPFaultException ex) { + // expected + } + + ((java.io.Closeable)greeter).close(); + } } From 06383eeb3d1ecdb3cf28424c262e11da0ae30600 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 26 Nov 2015 21:44:37 +0000 Subject: [PATCH 0134/1346] Adding Ehcache provider close method --- .../oauth2/grants/code/DefaultEHCacheCodeDataProvider.java | 5 +++-- .../oauth2/provider/DefaultEHCacheOAuthDataProvider.java | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java index 14193293493..ceb4ba0ea33 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java @@ -21,6 +21,7 @@ import net.sf.ehcache.Ehcache; import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; import org.apache.cxf.rs.security.oauth2.provider.DefaultEHCacheOAuthDataProvider; import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; @@ -32,7 +33,7 @@ public class DefaultEHCacheCodeDataProvider extends DefaultEHCacheOAuthDataProvi private Ehcache codeGrantCache; protected DefaultEHCacheCodeDataProvider() { - this(DEFAULT_CONFIG_URL, null); + this(DEFAULT_CONFIG_URL, BusFactory.getThreadDefaultBus(true)); } protected DefaultEHCacheCodeDataProvider(String configFileURL, Bus bus) { @@ -72,7 +73,7 @@ public ServerAuthorizationCodeGrant removeCodeGrant(String code) throws OAuthSer protected void saveCodeGrant(ServerAuthorizationCodeGrant grant) { putCacheValue(codeGrantCache, grant.getCode(), grant, grant.getExpiresIn()); } - + public void setCodeLifetime(long codeLifetime) { this.codeLifetime = codeLifetime; } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java index 23891d22ff5..f78d012d4eb 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java @@ -54,7 +54,7 @@ public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider private Ehcache refreshTokenCache; public DefaultEHCacheOAuthDataProvider() { - this(DEFAULT_CONFIG_URL, null); + this(DEFAULT_CONFIG_URL, BusFactory.getThreadDefaultBus(true)); } public DefaultEHCacheOAuthDataProvider(String configFileURL, Bus bus) { @@ -193,5 +193,8 @@ private void createCaches(String configFile, Bus bus, refreshTokenCache = createCache(cacheManager, refreshTokenKey); } - + public void close() { + cacheManager.shutdown(); + } + } From e81610d0c3518b6387c18a7ddb5d7363b36fdd07 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 27 Nov 2015 11:21:32 +0000 Subject: [PATCH 0135/1346] Allow setting the security context up with a SAML 1.1 assertion --- .../cxf/rs/security/saml/SAMLUtils.java | 66 +++++++++++++++---- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java index 60c755d1c66..b9aa7425d38 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java @@ -40,6 +40,11 @@ import org.apache.wss4j.common.saml.SAMLCallback; import org.apache.wss4j.common.saml.SAMLUtil; import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.opensaml.saml.saml1.core.AttributeStatement; +import org.opensaml.saml.saml1.core.AuthenticationStatement; +import org.opensaml.saml.saml1.core.AuthorizationDecisionStatement; +import org.opensaml.saml.saml1.core.NameIdentifier; +import org.opensaml.saml.saml1.core.Statement; import org.opensaml.saml.saml2.core.NameID; public final class SAMLUtils { @@ -51,18 +56,55 @@ private SAMLUtils() { } public static Subject getSubject(Message message, SamlAssertionWrapper assertionW) { - org.opensaml.saml.saml2.core.Subject s = assertionW.getSaml2().getSubject(); - Subject subject = new Subject(); - NameID nameId = s.getNameID(); - subject.setNameQualifier(nameId.getNameQualifier()); - // if format is transient then we may need to use STSClient - // to request an alternate name from IDP - subject.setNameFormat(nameId.getFormat()); - - subject.setName(nameId.getValue()); - subject.setSpId(nameId.getSPProvidedID()); - subject.setSpQualifier(nameId.getSPNameQualifier()); - return subject; + if (assertionW.getSaml2() != null) { + org.opensaml.saml.saml2.core.Subject s = assertionW.getSaml2().getSubject(); + Subject subject = new Subject(); + NameID nameId = s.getNameID(); + subject.setNameQualifier(nameId.getNameQualifier()); + // if format is transient then we may need to use STSClient + // to request an alternate name from IDP + subject.setNameFormat(nameId.getFormat()); + + subject.setName(nameId.getValue()); + subject.setSpId(nameId.getSPProvidedID()); + subject.setSpQualifier(nameId.getSPNameQualifier()); + return subject; + } else if (assertionW.getSaml1() != null) { + org.opensaml.saml.saml1.core.Subject s = getSaml1Subject(assertionW); + if (s != null) { + Subject subject = new Subject(); + NameIdentifier nameId = s.getNameIdentifier(); + subject.setNameQualifier(nameId.getNameQualifier()); + // if format is transient then we may need to use STSClient + // to request an alternate name from IDP + subject.setNameFormat(nameId.getFormat()); + + subject.setName(nameId.getValue()); + return subject; + } + } + return null; + } + + private static org.opensaml.saml.saml1.core.Subject getSaml1Subject(SamlAssertionWrapper assertionW) { + for (Statement stmt : ((org.opensaml.saml.saml1.core.Assertion)assertionW.getSaml1()).getStatements()) { + org.opensaml.saml.saml1.core.Subject samlSubject = null; + if (stmt instanceof AttributeStatement) { + AttributeStatement attrStmt = (AttributeStatement) stmt; + samlSubject = attrStmt.getSubject(); + } else if (stmt instanceof AuthenticationStatement) { + AuthenticationStatement authStmt = (AuthenticationStatement) stmt; + samlSubject = authStmt.getSubject(); + } else { + AuthorizationDecisionStatement authzStmt = + (AuthorizationDecisionStatement)stmt; + samlSubject = authzStmt.getSubject(); + } + if (samlSubject != null) { + return samlSubject; + } + } + return null; } public static SamlAssertionWrapper createAssertion(Message message) throws Fault { From 6c9121bedf128099603305107dc7b54e09a6f2fa Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 27 Nov 2015 11:25:08 +0000 Subject: [PATCH 0136/1346] Explicitly disallow SAML 1.1 in OAuth --- .../cxf/rs/security/oauth2/saml/SamlOAuthValidator.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java index ffb87193276..5a87fd447d0 100644 --- a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java +++ b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java @@ -62,6 +62,7 @@ public void setClientAddress(String value) { } public void validate(Message message, SamlAssertionWrapper wrapper) { + validateSAMLVersion(wrapper); Conditions cs = wrapper.getSaml2().getConditions(); validateAudience(message, cs); @@ -79,6 +80,12 @@ public void validate(Message message, SamlAssertionWrapper wrapper) { } } + private void validateSAMLVersion(SamlAssertionWrapper assertionW) { + if (assertionW.getSaml2() == null) { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } + } + private String getIssuer(SamlAssertionWrapper assertionW) { Issuer samlIssuer = assertionW.getSaml2().getIssuer(); return samlIssuer == null ? null : samlIssuer.getValue(); From 6b355293a7510afc7a40372e0e4b0265cfc3d0f2 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Fri, 27 Nov 2015 12:04:48 +0000 Subject: [PATCH 0137/1346] Avoid a bug that a bearer subject conf is not enforced if the subject conf list is not empty --- .../security/oauth2/saml/SamlOAuthValidator.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java index 5a87fd447d0..48830b02177 100644 --- a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java +++ b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/saml/SamlOAuthValidator.java @@ -124,17 +124,18 @@ private String getAbsoluteTargetAddress(Message m) { private boolean validateAuthenticationSubject(Message m, Conditions cs, org.opensaml.saml.saml2.core.Subject subject) { - if (subject.getSubjectConfirmations() == null) { - return false; - } // We need to find a Bearer Subject Confirmation method - for (SubjectConfirmation subjectConf : subject.getSubjectConfirmations()) { - if (SAML2Constants.CONF_BEARER.equals(subjectConf.getMethod())) { - validateSubjectConfirmation(m, cs, subjectConf.getSubjectConfirmationData()); + boolean bearerSubjectConfFound = false; + if (subject.getSubjectConfirmations() != null) { + for (SubjectConfirmation subjectConf : subject.getSubjectConfirmations()) { + if (SAML2Constants.CONF_BEARER.equals(subjectConf.getMethod())) { + validateSubjectConfirmation(m, cs, subjectConf.getSubjectConfirmationData()); + bearerSubjectConfFound = true; + } } } - return true; + return bearerSubjectConfFound; } /** From 9ef24a17c0085974c129d1739934eaca84b05e88 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 27 Nov 2015 13:41:56 +0000 Subject: [PATCH 0138/1346] Supporting overriding OAuth data providers in a more fine grained fashion and non-recycled refresh tokens --- .../grants/code/AbstractCodeDataProvider.java | 7 +- .../code/DefaultEHCacheCodeDataProvider.java | 7 +- .../provider/AbstractOAuthDataProvider.java | 130 ++++++++++++------ .../DefaultEHCacheOAuthDataProvider.java | 17 ++- .../DefaultEncryptingOAuthDataProvider.java | 30 ++-- 5 files changed, 129 insertions(+), 62 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java index fff16fa84b8..6bed9764e18 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java @@ -31,11 +31,16 @@ protected AbstractCodeDataProvider() { @Override public ServerAuthorizationCodeGrant createCodeGrant(AuthorizationCodeRegistration reg) throws OAuthServiceException { - ServerAuthorizationCodeGrant grant = initCodeGrant(reg, codeLifetime); + ServerAuthorizationCodeGrant grant = doCreateCodeGrant(reg); saveCodeGrant(grant); return grant; } + protected ServerAuthorizationCodeGrant doCreateCodeGrant(AuthorizationCodeRegistration reg) + throws OAuthServiceException { + return AbstractCodeDataProvider.initCodeGrant(reg, codeLifetime); + } + public void setCodeLifetime(long codeLifetime) { this.codeLifetime = codeLifetime; } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java index ceb4ba0ea33..5834a2c6cd0 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/DefaultEHCacheCodeDataProvider.java @@ -54,10 +54,15 @@ protected DefaultEHCacheCodeDataProvider(String configFileURL, @Override public ServerAuthorizationCodeGrant createCodeGrant(AuthorizationCodeRegistration reg) throws OAuthServiceException { - ServerAuthorizationCodeGrant grant = AbstractCodeDataProvider.initCodeGrant(reg, codeLifetime); + ServerAuthorizationCodeGrant grant = doCreateCodeGrant(reg); saveCodeGrant(grant); return grant; } + + protected ServerAuthorizationCodeGrant doCreateCodeGrant(AuthorizationCodeRegistration reg) + throws OAuthServiceException { + return AbstractCodeDataProvider.initCodeGrant(reg, codeLifetime); + } @Override public ServerAuthorizationCodeGrant removeCodeGrant(String code) throws OAuthServiceException { 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 0346c090c8f..c951c6e5c9c 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 @@ -34,14 +34,33 @@ public abstract class AbstractOAuthDataProvider implements OAuthDataProvider { private long accessTokenLifetime = 3600L; private long refreshTokenLifetime; // refresh tokens are eternal by default + private boolean recycleRefreshTokens = true; protected AbstractOAuthDataProvider() { } @Override - public ServerAccessToken createAccessToken(AccessTokenRegistration accessToken) + public ServerAccessToken createAccessToken(AccessTokenRegistration reg) throws OAuthServiceException { - return doCreateAccessToken(accessToken); + ServerAccessToken at = doCreateAccessToken(reg); + saveAccessToken(at); + if (isRefreshTokenSupported(reg.getApprovedScope())) { + createNewRefreshToken(at); + } + return at; + } + + protected ServerAccessToken doCreateAccessToken(AccessTokenRegistration accessToken) { + ServerAccessToken at = createNewAccessToken(accessToken.getClient()); + at.setAudience(accessToken.getAudience()); + at.setGrantType(accessToken.getGrantType()); + List theScopes = accessToken.getApprovedScope(); + List thePermissions = + convertScopeToPermissions(accessToken.getClient(), theScopes); + at.setScopes(thePermissions); + at.setSubject(accessToken.getSubject()); + at.setClientCodeVerifier(accessToken.getClientCodeVerifier()); + return at; } @Override @@ -52,30 +71,61 @@ public void removeAccessToken(ServerAccessToken token) throws OAuthServiceExcept @Override public ServerAccessToken refreshAccessToken(Client client, String refreshTokenKey, List restrictedScopes) throws OAuthServiceException { - RefreshToken oldRefreshToken = revokeRefreshAndAccessTokens(client, refreshTokenKey); - if (oldRefreshToken == null - || OAuthUtils.isExpired(oldRefreshToken.getIssuedAt(), oldRefreshToken.getExpiresIn())) { - throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED); + RefreshToken currentRefreshToken = revokeRefreshAndAccessTokens(client, refreshTokenKey); + ServerAccessToken at = doRefreshAccessToken(client, currentRefreshToken, restrictedScopes); + saveAccessToken(at); + if (recycleRefreshTokens) { + createNewRefreshToken(at); + } else { + updateRefreshToken(currentRefreshToken, at); } - return doRefreshAccessToken(client, oldRefreshToken, restrictedScopes); - + return at; } @Override public void revokeToken(Client client, String tokenKey, String tokenTypeHint) throws OAuthServiceException { - if (revokeAccessToken(tokenKey)) { - return; + ServerAccessToken accessToken = revokeAccessToken(tokenKey); + if (accessToken == null) { + doRevokeRefreshAndAccessTokens(client, tokenKey, true); + } else { + if (accessToken.getRefreshToken() != null) { + RefreshToken rt = getRefreshToken(client, accessToken.getRefreshToken()); + if (rt == null) { + return; + } + + List accessTokenKeys = rt.getAccessTokens(); + for (int i = 0; i < accessTokenKeys.size(); i++) { + if (accessTokenKeys.get(i).equals(accessToken.getTokenKey())) { + accessTokenKeys.remove(i); + break; + } + } + if (rt.getAccessTokens().isEmpty()) { + revokeRefreshToken(client, rt.getTokenKey()); + } else { + saveRefreshToken(null, rt); + } + } } - revokeRefreshAndAccessTokens(client, tokenKey); } protected RefreshToken revokeRefreshAndAccessTokens(Client client, String tokenKey) { - RefreshToken oldRefreshToken = revokeRefreshToken(client, tokenKey); - if (oldRefreshToken != null) { - for (String accessTokenKey : oldRefreshToken.getAccessTokens()) { - revokeAccessToken(accessTokenKey); - } + return doRevokeRefreshAndAccessTokens(client, tokenKey, recycleRefreshTokens); + } + protected RefreshToken doRevokeRefreshAndAccessTokens(Client client, String tokenKey, boolean recycle) { + RefreshToken currentRefreshToken = recycle ? revokeRefreshToken(client, tokenKey) + : getRefreshToken(client, tokenKey); + if (currentRefreshToken == null + || OAuthUtils.isExpired(currentRefreshToken.getIssuedAt(), currentRefreshToken.getExpiresIn())) { + throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED); + } + for (String accessTokenKey : currentRefreshToken.getAccessTokens()) { + revokeAccessToken(accessTokenKey); } - return oldRefreshToken; + if (recycle) { + currentRefreshToken.getAccessTokens().clear(); + } + return currentRefreshToken; } @@ -96,23 +146,6 @@ public ServerAccessToken getPreauthorizedToken(Client client, List reque return null; } - protected ServerAccessToken doCreateAccessToken(AccessTokenRegistration accessToken) { - ServerAccessToken at = createNewAccessToken(accessToken.getClient()); - at.setAudience(accessToken.getAudience()); - at.setGrantType(accessToken.getGrantType()); - List theScopes = accessToken.getApprovedScope(); - List thePermissions = - convertScopeToPermissions(accessToken.getClient(), theScopes); - at.setScopes(thePermissions); - at.setSubject(accessToken.getSubject()); - at.setClientCodeVerifier(accessToken.getClientCodeVerifier()); - saveAccessToken(at); - if (isRefreshTokenSupported(theScopes)) { - createNewRefreshToken(at); - } - return at; - } - protected boolean isRefreshTokenSupported(List theScopes) { return theScopes.contains(OAuthConstants.REFRESH_TOKEN_SCOPE); } @@ -121,19 +154,32 @@ protected ServerAccessToken createNewAccessToken(Client client) { return new BearerAccessToken(client, accessTokenLifetime); } + protected RefreshToken updateRefreshToken(RefreshToken rt, ServerAccessToken at) { + linkRefreshAccessTokens(rt, at); + saveRefreshToken(at, rt); + return rt; + } protected RefreshToken createNewRefreshToken(ServerAccessToken at) { + RefreshToken rt = doCreateNewRefreshToken(at); + saveRefreshToken(at, rt); + return rt; + } + protected RefreshToken doCreateNewRefreshToken(ServerAccessToken at) { RefreshToken rt = new RefreshToken(at.getClient(), refreshTokenLifetime); rt.setAudience(at.getAudience()); rt.setGrantType(at.getGrantType()); rt.setScopes(at.getScopes()); rt.setSubject(at.getSubject()); rt.setClientCodeVerifier(at.getClientCodeVerifier()); - rt.getAccessTokens().add(at.getTokenKey()); - at.setRefreshToken(rt.getTokenKey()); - saveRefreshToken(at, rt); + linkRefreshAccessTokens(rt, at); return rt; } + private void linkRefreshAccessTokens(RefreshToken rt, ServerAccessToken at) { + rt.getAccessTokens().add(at.getTokenKey()); + at.setRefreshToken(rt.getTokenKey()); + } + protected ServerAccessToken doRefreshAccessToken(Client client, RefreshToken oldRefreshToken, List restrictedScopes) { @@ -151,8 +197,6 @@ protected ServerAccessToken doRefreshAccessToken(Client client, throw new OAuthServiceException("Invalid scopes"); } } - saveAccessToken(at); - createNewRefreshToken(at); return at; } @@ -164,9 +208,13 @@ public void setRefreshTokenLifetime(long refreshTokenLifetime) { this.refreshTokenLifetime = refreshTokenLifetime; } + public void setRecycleRefreshTokens(boolean recycleRefreshTokens) { + this.recycleRefreshTokens = recycleRefreshTokens; + } + protected abstract void saveAccessToken(ServerAccessToken serverToken); protected abstract void saveRefreshToken(ServerAccessToken at, RefreshToken refreshToken); - protected abstract boolean revokeAccessToken(String accessTokenKey); + protected abstract ServerAccessToken revokeAccessToken(String accessTokenKey); protected abstract RefreshToken revokeRefreshToken(Client client, String refreshTokenKey); - + protected abstract RefreshToken getRefreshToken(Client client, String refreshTokenKey); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java index f78d012d4eb..3e9e8006364 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java @@ -102,17 +102,20 @@ public ServerAccessToken getAccessToken(String accessToken) throws OAuthServiceE return getCacheValue(accessTokenCache, accessToken, ServerAccessToken.class); } - @Override - public void removeAccessToken(ServerAccessToken accessToken) throws OAuthServiceException { - revokeAccessToken(accessToken.getTokenKey()); + protected ServerAccessToken revokeAccessToken(String accessTokenKey) { + ServerAccessToken at = getAccessToken(accessTokenKey); + if (at != null) { + accessTokenCache.remove(accessTokenKey); + } + return at; } - - protected boolean revokeAccessToken(String accessTokenKey) { - return accessTokenCache.remove(accessTokenKey); + + protected RefreshToken getRefreshToken(Client client, String refreshTokenKey) { + return getCacheValue(refreshTokenCache, refreshTokenKey, RefreshToken.class); } protected RefreshToken revokeRefreshToken(Client client, String refreshTokenKey) { - RefreshToken refreshToken = getCacheValue(refreshTokenCache, refreshTokenKey, RefreshToken.class); + RefreshToken refreshToken = getRefreshToken(client, refreshTokenKey); if (refreshToken != null) { refreshTokenCache.remove(refreshTokenKey); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java index d033b1f3f0a..ed968ad7273 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java @@ -85,19 +85,16 @@ public ServerAccessToken getAccessToken(String accessToken) throws OAuthServiceE } } - @Override - public void removeAccessToken(ServerAccessToken accessToken) throws OAuthServiceException { - revokeAccessToken(accessToken.getTokenKey()); - } - @Override protected void saveAccessToken(ServerAccessToken serverToken) { encryptAccessToken(serverToken); } @Override - protected boolean revokeAccessToken(String accessTokenKey) { - return tokens.remove(accessTokenKey); + protected ServerAccessToken revokeAccessToken(String accessTokenKey) { + ServerAccessToken at = getAccessToken(accessTokenKey); + tokens.remove(accessTokenKey); + return at; } @Override @@ -108,12 +105,13 @@ protected void saveRefreshToken(ServerAccessToken at, RefreshToken refreshToken) @Override protected RefreshToken revokeRefreshToken(Client client, String refreshTokenKey) { - refreshTokens.remove(refreshTokenKey); - try { - return ModelEncryptionSupport.decryptRefreshToken(this, refreshTokenKey, key); - } catch (SecurityException ex) { - throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED, ex); + RefreshToken rt = null; + if (refreshTokens.containsKey(refreshTokenKey)) { + rt = getRefreshToken(client, refreshTokenKey); + refreshTokens.remove(refreshTokenKey); } + return rt; + } private void encryptAccessToken(ServerAccessToken token) { @@ -122,4 +120,12 @@ private void encryptAccessToken(ServerAccessToken token) { refreshTokens.put(token.getRefreshToken(), encryptedToken); token.setTokenKey(encryptedToken); } + @Override + protected RefreshToken getRefreshToken(Client client, String refreshTokenKey) { + try { + return ModelEncryptionSupport.decryptRefreshToken(this, refreshTokenKey, key); + } catch (SecurityException ex) { + throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED, ex); + } + } } From 3912113d9f0d45505987d36b95395a833bcbe4f4 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 27 Nov 2015 14:39:46 +0000 Subject: [PATCH 0139/1346] Minor updates to OAuth abstract provider --- .../provider/AbstractOAuthDataProvider.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) 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 c951c6e5c9c..7fac0b42868 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 @@ -86,21 +86,17 @@ public ServerAccessToken refreshAccessToken(Client client, String refreshTokenKe public void revokeToken(Client client, String tokenKey, String tokenTypeHint) throws OAuthServiceException { ServerAccessToken accessToken = revokeAccessToken(tokenKey); if (accessToken == null) { + // Revoke refresh token doRevokeRefreshAndAccessTokens(client, tokenKey, true); } else { + // Revoke access token if (accessToken.getRefreshToken() != null) { RefreshToken rt = getRefreshToken(client, accessToken.getRefreshToken()); if (rt == null) { return; } - List accessTokenKeys = rt.getAccessTokens(); - for (int i = 0; i < accessTokenKeys.size(); i++) { - if (accessTokenKeys.get(i).equals(accessToken.getTokenKey())) { - accessTokenKeys.remove(i); - break; - } - } + unlinkRefreshAccessToken(rt, accessToken.getTokenKey()); if (rt.getAccessTokens().isEmpty()) { revokeRefreshToken(client, rt.getTokenKey()); } else { @@ -109,6 +105,16 @@ public void revokeToken(Client client, String tokenKey, String tokenTypeHint) th } } } + protected void unlinkRefreshAccessToken(RefreshToken rt, String tokenKey) { + List accessTokenKeys = rt.getAccessTokens(); + for (int i = 0; i < accessTokenKeys.size(); i++) { + if (accessTokenKeys.get(i).equals(tokenKey)) { + accessTokenKeys.remove(i); + break; + } + } + } + protected RefreshToken revokeRefreshAndAccessTokens(Client client, String tokenKey) { return doRevokeRefreshAndAccessTokens(client, tokenKey, recycleRefreshTokens); } @@ -119,11 +125,10 @@ protected RefreshToken doRevokeRefreshAndAccessTokens(Client client, String toke || OAuthUtils.isExpired(currentRefreshToken.getIssuedAt(), currentRefreshToken.getExpiresIn())) { throw new OAuthServiceException(OAuthConstants.ACCESS_DENIED); } - for (String accessTokenKey : currentRefreshToken.getAccessTokens()) { - revokeAccessToken(accessTokenKey); - } if (recycle) { - currentRefreshToken.getAccessTokens().clear(); + for (String accessTokenKey : currentRefreshToken.getAccessTokens()) { + revokeAccessToken(accessTokenKey); + } } return currentRefreshToken; } From 5902802031e7cf156fe5f0c7a804ec1a53127c27 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 27 Nov 2015 16:39:58 +0000 Subject: [PATCH 0140/1346] Getting all AbstractOAuthDataProvider implement ClientRegistrartionInterface --- .../rs/security/oauth2/provider/AbstractOAuthDataProvider.java | 2 +- .../oauth2/provider/DefaultEHCacheOAuthDataProvider.java | 3 +-- .../oauth2/provider/DefaultEncryptingOAuthDataProvider.java | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) 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 7fac0b42868..b77dce9df51 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 @@ -31,7 +31,7 @@ import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; -public abstract class AbstractOAuthDataProvider implements OAuthDataProvider { +public abstract class AbstractOAuthDataProvider implements OAuthDataProvider, ClientRegistrationProvider { private long accessTokenLifetime = 3600L; private long refreshTokenLifetime; // refresh tokens are eternal by default private boolean recycleRefreshTokens = true; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java index 3e9e8006364..bdf9d101fab 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEHCacheOAuthDataProvider.java @@ -41,8 +41,7 @@ import org.apache.cxf.rs.security.oauth2.tokens.refresh.RefreshToken; import org.apache.cxf.rs.security.oauth2.utils.EHCacheUtil; -public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider - implements ClientRegistrationProvider { +public class DefaultEHCacheOAuthDataProvider extends AbstractOAuthDataProvider { public static final String CLIENT_CACHE_KEY = "cxf.oauth2.client.cache"; public static final String ACCESS_TOKEN_CACHE_KEY = "cxf.oauth2.accesstoken.cache"; public static final String REFRESH_TOKEN_CACHE_KEY = "cxf.oauth2.refreshtoken.cache"; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java index ed968ad7273..7d9e4d2a275 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/DefaultEncryptingOAuthDataProvider.java @@ -35,8 +35,7 @@ import org.apache.cxf.rt.security.crypto.CryptoUtils; import org.apache.cxf.rt.security.crypto.KeyProperties; -public class DefaultEncryptingOAuthDataProvider extends AbstractOAuthDataProvider - implements ClientRegistrationProvider { +public class DefaultEncryptingOAuthDataProvider extends AbstractOAuthDataProvider { protected SecretKey key; private Set tokens = Collections.synchronizedSet(new HashSet()); private ConcurrentHashMap refreshTokens = new ConcurrentHashMap(); From 3aaf7d75c873c990d8e86d27e37f03c47e074175 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 27 Nov 2015 17:06:31 +0000 Subject: [PATCH 0141/1346] Distinguishing between RS authorization and the initial token validation failures, though returning a dedicated type will probbaly need to be done --- .../security/oauth2/common/AccessTokenValidation.java | 9 +++++++++ .../security/oauth2/filters/OAuthRequestFilter.java | 4 +++- .../oauth2/services/AccessTokenValidatorService.java | 11 ++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java index f25f286f0f2..5fb14a63261 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/common/AccessTokenValidation.java @@ -36,6 +36,7 @@ // (introduce default constructors, etc) @XmlRootElement public class AccessTokenValidation { + private boolean initialValidationSuccessful = true; private String clientId; private String clientIpAddress; private UserSubject clientSubject; @@ -167,5 +168,13 @@ public String getClientCodeVerifier() { public void setClientCodeVerifier(String clientCodeVerifier) { this.clientCodeVerifier = clientCodeVerifier; } + + public boolean isInitialValidationSuccessful() { + return initialValidationSuccessful; + } + + public void setInitialValidationSuccessful(boolean localValidationSuccessful) { + this.initialValidationSuccessful = localValidationSuccessful; + } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java index c11cbc2aa99..d1a479c3416 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java @@ -92,7 +92,9 @@ protected void validateRequest(Message m) { // Get the access token AccessTokenValidation accessTokenV = getAccessTokenValidation(authScheme, authSchemeData, null); - + if (!accessTokenV.isInitialValidationSuccessful()) { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } // Find the scopes which match the current request List permissions = accessTokenV.getTokenScopes(); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenValidatorService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenValidatorService.java index 67609fab226..d87dd2f94d6 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenValidatorService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AccessTokenValidatorService.java @@ -22,6 +22,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.Encoded; +import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; @@ -46,7 +47,15 @@ public AccessTokenValidation getTokenValidationInfo(@Encoded MultivaluedMap Date: Mon, 30 Nov 2015 12:14:39 +0000 Subject: [PATCH 0142/1346] Propagating a nonce value from a code grant to an access token reg --- .../oauth2/grants/AbstractGrantHandler.java | 15 ++++++++------- .../grants/code/AbstractCodeDataProvider.java | 1 + .../code/AuthorizationCodeGrantHandler.java | 3 ++- .../grants/code/ServerAuthorizationCodeGrant.java | 9 +++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java index b855af0bd93..2300b1147cb 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java @@ -108,8 +108,7 @@ protected ServerAccessToken doCreateAccessToken(Client client, UserSubject subject, List requestedScope) { - return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope, - null, null, null); + return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope); } protected ServerAccessToken doCreateAccessToken(Client client, @@ -119,23 +118,25 @@ protected ServerAccessToken doCreateAccessToken(Client client, String audience) { return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope, - approvedScope, audience, null); + approvedScope, audience, null, null); } protected ServerAccessToken doCreateAccessToken(Client client, UserSubject subject, String requestedGrant, List requestedScope) { - return doCreateAccessToken(client, subject, requestedGrant, requestedScope, null, null, null); + return doCreateAccessToken(client, subject, requestedGrant, requestedScope, null, null, null, null); } - + //CHECKSTYLE:OFF protected ServerAccessToken doCreateAccessToken(Client client, UserSubject subject, String requestedGrant, List requestedScope, List approvedScope, String audience, - String codeVerifier) { + String codeVerifier, + String nonce) { + //CHECKSTYLE:ON if (!OAuthUtils.validateScopes(requestedScope, client.getRegisteredScopes(), partialMatchScopeValidation)) { throw new OAuthServiceException(new OAuthError(OAuthConstants.INVALID_SCOPE)); @@ -163,7 +164,7 @@ protected ServerAccessToken doCreateAccessToken(Client client, reg.setApprovedScope(approvedScope); reg.setAudience(audience); reg.setClientCodeVerifier(codeVerifier); - + reg.setNonce(nonce); return dataProvider.createAccessToken(reg); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java index 6bed9764e18..1b63bb3e2aa 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java @@ -55,6 +55,7 @@ public static ServerAuthorizationCodeGrant initCodeGrant(AuthorizationCodeRegist grant.setApprovedScopes(reg.getApprovedScope()); grant.setAudience(reg.getAudience()); grant.setClientCodeChallenge(reg.getClientCodeChallenge()); + grant.setNonce(reg.getNonce()); return grant; } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java index 9a6888a6285..f2cf4995791 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java @@ -84,7 +84,8 @@ public ServerAccessToken createAccessToken(Client client, MultivaluedMap requestedScopes = Collections.emptyList(); private UserSubject subject; private String audience; + private String nonce; private String clientCodeChallenge; public ServerAuthorizationCodeGrant() { @@ -165,4 +166,12 @@ public List getRequestedScopes() { public void setRequestedScopes(List requestedScopes) { this.requestedScopes = requestedScopes; } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } } From 04771f9b93c86197b194791d502fd224dbb1eacc Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 30 Nov 2015 12:37:45 +0000 Subject: [PATCH 0143/1346] Reverting most of the last nonce-related changes, keeping a minor AbstractGrantHandler optimization --- .../security/oauth2/grants/AbstractGrantHandler.java | 10 +++------- .../oauth2/grants/code/AbstractCodeDataProvider.java | 1 - .../grants/code/AuthorizationCodeGrantHandler.java | 3 +-- .../grants/code/ServerAuthorizationCodeGrant.java | 9 --------- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java index 2300b1147cb..38ab6907436 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/AbstractGrantHandler.java @@ -118,25 +118,22 @@ protected ServerAccessToken doCreateAccessToken(Client client, String audience) { return doCreateAccessToken(client, subject, getSingleGrantType(), requestedScope, - approvedScope, audience, null, null); + approvedScope, audience, null); } protected ServerAccessToken doCreateAccessToken(Client client, UserSubject subject, String requestedGrant, List requestedScope) { - return doCreateAccessToken(client, subject, requestedGrant, requestedScope, null, null, null, null); + return doCreateAccessToken(client, subject, requestedGrant, requestedScope, null, null, null); } - //CHECKSTYLE:OFF protected ServerAccessToken doCreateAccessToken(Client client, UserSubject subject, String requestedGrant, List requestedScope, List approvedScope, String audience, - String codeVerifier, - String nonce) { - //CHECKSTYLE:ON + String codeVerifier) { if (!OAuthUtils.validateScopes(requestedScope, client.getRegisteredScopes(), partialMatchScopeValidation)) { throw new OAuthServiceException(new OAuthError(OAuthConstants.INVALID_SCOPE)); @@ -164,7 +161,6 @@ protected ServerAccessToken doCreateAccessToken(Client client, reg.setApprovedScope(approvedScope); reg.setAudience(audience); reg.setClientCodeVerifier(codeVerifier); - reg.setNonce(nonce); return dataProvider.createAccessToken(reg); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java index 1b63bb3e2aa..6bed9764e18 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AbstractCodeDataProvider.java @@ -55,7 +55,6 @@ public static ServerAuthorizationCodeGrant initCodeGrant(AuthorizationCodeRegist grant.setApprovedScopes(reg.getApprovedScope()); grant.setAudience(reg.getAudience()); grant.setClientCodeChallenge(reg.getClientCodeChallenge()); - grant.setNonce(reg.getNonce()); return grant; } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java index f2cf4995791..9a6888a6285 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java @@ -84,8 +84,7 @@ public ServerAccessToken createAccessToken(Client client, MultivaluedMap requestedScopes = Collections.emptyList(); private UserSubject subject; private String audience; - private String nonce; private String clientCodeChallenge; public ServerAuthorizationCodeGrant() { @@ -166,12 +165,4 @@ public List getRequestedScopes() { public void setRequestedScopes(List requestedScopes) { this.requestedScopes = requestedScopes; } - - public String getNonce() { - return nonce; - } - - public void setNonce(String nonce) { - this.nonce = nonce; - } } From 9a648a0e41eda218c8cfeff0defdd028ba697558 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 30 Nov 2015 12:57:18 +0000 Subject: [PATCH 0144/1346] Some minor changes to the SAML Grant Handler --- .../grants/saml/Saml2BearerGrantHandler.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java index 71b876a9424..6d97d72ec5b 100644 --- a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java @@ -38,7 +38,6 @@ import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.jaxrs.utils.HttpUtils; import org.apache.cxf.message.Message; -import org.apache.cxf.message.MessageUtils; import org.apache.cxf.phase.PhaseInterceptorChain; import org.apache.cxf.rs.security.common.CryptoLoader; import org.apache.cxf.rs.security.common.RSSecurityUtils; @@ -64,7 +63,6 @@ import org.apache.wss4j.dom.WSDocInfo; import org.apache.wss4j.dom.engine.WSSConfig; import org.apache.wss4j.dom.handler.RequestData; -import org.apache.wss4j.dom.handler.WSHandlerConstants; import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor; import org.apache.wss4j.dom.validate.Credential; import org.apache.wss4j.dom.validate.SamlAssertionValidator; @@ -187,8 +185,15 @@ protected void validateToken(Message message, SamlAssertionWrapper assertion) { } catch (IOException ex) { throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); } - data.setEnableRevocation(MessageUtils.isTrue( - message.getContextualProperty(WSHandlerConstants.ENABLE_REVOCATION))); + + boolean enableRevocation = false; + String enableRevocationStr = + (String)org.apache.cxf.rt.security.utils.SecurityUtils.getSecurityPropertyValue( + SecurityConstants.ENABLE_REVOCATION, message); + if (enableRevocationStr != null) { + enableRevocation = Boolean.parseBoolean(enableRevocationStr); + } + data.setEnableRevocation(enableRevocation); Signature sig = assertion.getSignature(); WSDocInfo docInfo = new WSDocInfo(sig.getDOM().getOwnerDocument()); @@ -200,7 +205,10 @@ protected void validateToken(Message message, SamlAssertionWrapper assertion) { data.getSigVerCrypto() ); assertion.verifySignature(samlKeyInfo); - + assertion.parseSubject( + new WSSSAMLKeyInfoProcessor(data, null), data.getSigVerCrypto(), + data.getCallbackHandler() + ); } else if (getTLSCertificates(message) == null) { throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); } From a52968023cb778958a84be0b3e6a5b8af507309f Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 30 Nov 2015 14:10:43 +0000 Subject: [PATCH 0145/1346] [CXF-6693] Support for a $ char in HttpHeaderImpl.getCookies --- .../cxf/jaxrs/impl/HttpHeadersImpl.java | 34 +++++++++++++++---- .../cxf/jaxrs/impl/HttpHeadersImplTest.java | 23 +++++++++++-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java index 4bcf4ff49b0..3990af4a96b 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/HttpHeadersImpl.java @@ -57,6 +57,10 @@ public class HttpHeadersImpl implements HttpHeaders { private static final String COOKIE_SEPARATOR_CRLF = "crlf"; private static final String DEFAULT_SEPARATOR = ","; private static final String DEFAULT_COOKIE_SEPARATOR = ";"; + private static final String DOLLAR_CHAR = "$"; + private static final String COOKIE_VERSION_PARAM = DOLLAR_CHAR + "Version"; + private static final String COOKIE_PATH_PARAM = DOLLAR_CHAR + "Path"; + private static final String COOKIE_DOMAIN_PARAM = DOLLAR_CHAR + "Domain"; private static final String COMPLEX_HEADER_EXPRESSION = "(([\\w]+=\"[^\"]*\")|([\\w]+=[\\w]+)|([\\w]+))(;(([\\w]+=\"[^\"]*\")|([\\w]+=[\\w]+)|([\\w]+)))?"; @@ -110,9 +114,10 @@ public Map getCookies() { if (value == null) { continue; } - List cs = value.contains("$") - ? Collections.singletonList(value) - : getHeaderValues(HttpHeaders.COOKIE, value, getCookieSeparator()); + + + List cs = getHeaderValues(HttpHeaders.COOKIE, value, + getCookieSeparator(value)); for (String c : cs) { Cookie cookie = Cookie.valueOf(c); cl.put(cookie.getName(), cookie); @@ -121,7 +126,22 @@ public Map getCookies() { return cl; } - private String getCookieSeparator() { + private String getCookieSeparator(String value) { + String separator = getCookieSeparatorFromProperty(); + if (separator != null) { + return separator; + } else { + if (value.contains(DOLLAR_CHAR) + && (value.contains(COOKIE_VERSION_PARAM) + || value.contains(COOKIE_PATH_PARAM) + || value.contains(COOKIE_DOMAIN_PARAM))) { + return DEFAULT_SEPARATOR; + } + + return DEFAULT_COOKIE_SEPARATOR; + } + } + private String getCookieSeparatorFromProperty() { Object cookiePropValue = message.getContextualProperty(COOKIE_SEPARATOR_PROPERTY); if (cookiePropValue != null) { String separator = cookiePropValue.toString().trim(); @@ -133,9 +153,9 @@ private String getCookieSeparator() { } return separator; } else { - return DEFAULT_COOKIE_SEPARATOR; + return null; } - } + } public Locale getLanguage() { List values = getListValues(HttpHeaders.CONTENT_LANGUAGE); @@ -212,7 +232,7 @@ public List getRequestHeader(String name) { if (value == null) { continue; } - String sep = HttpHeaders.COOKIE.equalsIgnoreCase(name) ? getCookieSeparator() : DEFAULT_SEPARATOR; + String sep = HttpHeaders.COOKIE.equalsIgnoreCase(name) ? getCookieSeparator(value) : DEFAULT_SEPARATOR; ls.addAll(getHeaderValues(name, value, sep)); } return ls; diff --git a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java index c82af5d0a27..314b87eee12 100644 --- a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java +++ b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/HttpHeadersImplTest.java @@ -396,12 +396,12 @@ public void testGetCookies() throws Exception { Message m = new MessageImpl(); m.setExchange(new ExchangeImpl()); MetadataMap headers = createHeaders(); - headers.putSingle(HttpHeaders.COOKIE, "a=b;c=d"); + headers.putSingle(HttpHeaders.COOKIE, "a=$b;c=d"); m.put(Message.PROTOCOL_HEADERS, headers); HttpHeaders h = new HttpHeadersImpl(m); Map cookies = h.getCookies(); assertEquals(2, cookies.size()); - assertEquals("b", cookies.get("a").getValue()); + assertEquals("$b", cookies.get("a").getValue()); assertEquals("d", cookies.get("c").getValue()); } @@ -421,6 +421,25 @@ public void testGetCookieWithAttributes() throws Exception { assertEquals(1, cookie.getVersion()); } + @Test + public void testGetCookiesWithAttributes() throws Exception { + + Message m = new MessageImpl(); + m.setExchange(new ExchangeImpl()); + MetadataMap headers = createHeaders(); + headers.putSingle(HttpHeaders.COOKIE, "$Version=1;a=b, $Version=1;c=d"); + m.put(Message.PROTOCOL_HEADERS, headers); + HttpHeaders h = new HttpHeadersImpl(m); + Map cookies = h.getCookies(); + assertEquals(2, cookies.size()); + Cookie cookieA = cookies.get("a"); + assertEquals("b", cookieA.getValue()); + assertEquals(1, cookieA.getVersion()); + Cookie cookieC = cookies.get("c"); + assertEquals("d", cookieC.getValue()); + assertEquals(1, cookieA.getVersion()); + } + @Test public void testGetCookiesWithComma() throws Exception { From aa0a0a21e10709412b7b0491706933654be69dcd Mon Sep 17 00:00:00 2001 From: Jan Bernhardt Date: Mon, 30 Nov 2015 15:46:04 +0100 Subject: [PATCH 0146/1346] Adding redirect_uri collection and name to oauth2 consumer --- .../rs/security/oauth2/client/Consumer.java | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java index caa456db581..e592ec9c0cf 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java @@ -18,44 +18,80 @@ */ package org.apache.cxf.rs.security.oauth2.client; +import java.util.HashSet; +import java.util.Set; + public class Consumer { - + private String key; private String secret; + private Set redirectURIs; + private String name; private String description; - + public Consumer() { - + } + public Consumer(String key, String secret) { this.setKey(key); this.setSecret(secret); } + public String getKey() { return key; } + public void setKey(String key) { this.key = key; } + public String getSecret() { return secret; } + public void setSecret(String secret) { this.secret = secret; } + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + @Override public int hashCode() { return key.hashCode(); } + @Override public boolean equals(Object o) { return o instanceof Consumer && key.equals(((Consumer)o).key); } - + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getRedirectURIs() { + return redirectURIs; + } + + public void setRedirectURIs(Set redirectUri) { + this.redirectURIs = redirectUri; + } + + public boolean addRedirectURI(String redirectURI) { + if (this.redirectURIs == null) { + this.redirectURIs = new HashSet(); + } + return this.redirectURIs.add(redirectURI); + } } From a400eaa95b977c292162e69434b98ebbf578febd Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Mon, 30 Nov 2015 17:14:45 +0000 Subject: [PATCH 0147/1346] Set a security context up from the JWS cert --- .../jose/jaxrs/JwsContainerRequestFilter.java | 26 +++++++++++++++++++ .../jose/jws/EcDsaJwsSignatureVerifier.java | 9 +++++++ .../cxf/rs/security/jose/jws/JwsUtils.java | 26 ++++++++++++++----- .../jws/PublicKeyJwsSignatureVerifier.java | 19 ++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java index ab2698f8f0f..003e674d936 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.security.Principal; import javax.annotation.Priority; import javax.ws.rs.HttpMethod; @@ -32,6 +33,8 @@ import org.apache.cxf.rs.security.jose.common.JoseUtils; import org.apache.cxf.rs.security.jose.jws.JwsCompactConsumer; import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier; +import org.apache.cxf.security.SecurityContext; @PreMatching @Priority(Priorities.JWS_SERVER_READ_PRIORITY) @@ -56,6 +59,29 @@ public void filter(ContainerRequestContext context) throws IOException { if (ct != null) { context.getHeaders().putSingle("Content-Type", ct); } + + SecurityContext securityContext = configureSecurityContext(theSigVerifier); + if (securityContext != null) { + JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext); + } } + protected SecurityContext configureSecurityContext(JwsSignatureVerifier sigVerifier) { + if (sigVerifier instanceof PublicKeyJwsSignatureVerifier + && ((PublicKeyJwsSignatureVerifier)sigVerifier).getX509Certificate() != null) { + final Principal principal = + ((PublicKeyJwsSignatureVerifier)sigVerifier).getX509Certificate().getSubjectX500Principal(); + return new SecurityContext() { + + public Principal getUserPrincipal() { + return principal; + } + + public boolean isUserInRole(String arg0) { + return false; + } + }; + } + return null; + } } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java index 025cd21060a..36e87999ba3 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureVerifier.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.jose.jws; import java.security.PublicKey; +import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.util.HashMap; import java.util.Map; @@ -40,6 +41,14 @@ public EcDsaJwsSignatureVerifier(PublicKey key, SignatureAlgorithm supportedAlgo public EcDsaJwsSignatureVerifier(PublicKey key, AlgorithmParameterSpec spec, SignatureAlgorithm supportedAlgo) { super(key, spec, supportedAlgo); } + public EcDsaJwsSignatureVerifier(X509Certificate cert, SignatureAlgorithm supportedAlgo) { + this(cert, null, supportedAlgo); + } + public EcDsaJwsSignatureVerifier(X509Certificate cert, + AlgorithmParameterSpec spec, + SignatureAlgorithm supportedAlgo) { + super(cert, spec, supportedAlgo); + } @Override public boolean verify(JwsHeaders headers, String unsignedText, byte[] signature) { final String algoName = super.getAlgorithm().getJwaName(); diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index 710baa7bef9..cc03efd311e 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -151,7 +151,20 @@ public static JwsSignatureVerifier getSignatureVerifier(JsonWebKey jwk, Signatur return theVerifier; } public static JwsSignatureVerifier getPublicKeySignatureVerifier(X509Certificate cert, SignatureAlgorithm algo) { - return getPublicKeySignatureVerifier(cert.getPublicKey(), algo); + if (algo == null) { + LOG.warning("No signature algorithm was defined"); + throw new JwsException(JwsException.Error.ALGORITHM_NOT_SET); + } + + if (cert != null) { + if (cert.getPublicKey() instanceof RSAPublicKey) { + return new PublicKeyJwsSignatureVerifier(cert, algo); + } else if (cert.getPublicKey() instanceof ECPublicKey) { + return new EcDsaJwsSignatureVerifier(cert, algo); + } + } + + return null; } public static JwsSignatureVerifier getPublicKeySignatureVerifier(PublicKey key, SignatureAlgorithm algo) { if (algo == null) { @@ -377,7 +390,7 @@ public static JwsSignatureVerifier loadSignatureVerifier(Message m, } else if (inHeaders.getHeader(JoseConstants.HEADER_X509_CHAIN) != null) { List chain = KeyManagementUtils.toX509CertificateChain(inHeaders.getX509Chain()); KeyManagementUtils.validateCertificateChain(props, chain); - return getPublicKeySignatureVerifier(chain.get(0).getPublicKey(), + return getPublicKeySignatureVerifier(chain.get(0), inHeaders.getSignatureAlgorithm()); } else if (inHeaders.getHeader(JoseConstants.HEADER_X509_THUMBPRINT) != null) { X509Certificate foundCert = @@ -385,7 +398,7 @@ public static JwsSignatureVerifier loadSignatureVerifier(Message m, MessageDigestUtils.ALGO_SHA_1, m, props); if (foundCert != null) { - return getPublicKeySignatureVerifier(foundCert.getPublicKey(), + return getPublicKeySignatureVerifier(foundCert, inHeaders.getSignatureAlgorithm()); } } @@ -406,9 +419,10 @@ public static JwsSignatureVerifier loadSignatureVerifier(Message m, && SignatureAlgorithm.NONE.getJwaName().equals(inHeaders.getAlgorithm())) { theVerifier = new NoneJwsSignatureVerifier(); } else { - theVerifier = getPublicKeySignatureVerifier( - KeyManagementUtils.loadPublicKey(m, props), - signatureAlgo); + X509Certificate[] certs = KeyManagementUtils.loadX509CertificateOrChain(m, props); + if (certs != null && certs.length > 0) { + theVerifier = getPublicKeySignatureVerifier(certs[0], signatureAlgo); + } } } if (theVerifier == null && !ignoreNullVerifier) { diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java index 65a2a1503d6..b58e74fa7ac 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java @@ -19,6 +19,7 @@ package org.apache.cxf.rs.security.jose.jws; import java.security.PublicKey; +import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.util.logging.Logger; @@ -33,6 +34,7 @@ public class PublicKeyJwsSignatureVerifier implements JwsSignatureVerifier { private PublicKey key; private AlgorithmParameterSpec signatureSpec; private SignatureAlgorithm supportedAlgo; + private X509Certificate cert; public PublicKeyJwsSignatureVerifier(PublicKey key, SignatureAlgorithm supportedAlgorithm) { this(key, null, supportedAlgorithm); @@ -43,6 +45,20 @@ public PublicKeyJwsSignatureVerifier(PublicKey key, AlgorithmParameterSpec spec, this.supportedAlgo = supportedAlgo; JwsUtils.checkSignatureKeySize(key); } + public PublicKeyJwsSignatureVerifier(X509Certificate cert, SignatureAlgorithm supportedAlgorithm) { + this(cert, null, supportedAlgorithm); + } + public PublicKeyJwsSignatureVerifier(X509Certificate cert, + AlgorithmParameterSpec spec, + SignatureAlgorithm supportedAlgo) { + if (cert != null) { + this.key = cert.getPublicKey(); + } + this.cert = cert; + this.signatureSpec = spec; + this.supportedAlgo = supportedAlgo; + JwsUtils.checkSignatureKeySize(key); + } @Override public boolean verify(JwsHeaders headers, String unsignedText, byte[] signature) { try { @@ -78,4 +94,7 @@ public SignatureAlgorithm getAlgorithm() { return supportedAlgo; } + public X509Certificate getX509Certificate() { + return cert; + } } From 2f5f034b8e8361aed103de5bd7fa7526847ac690 Mon Sep 17 00:00:00 2001 From: Akitoshi Yoshida Date: Tue, 1 Dec 2015 10:57:13 +0100 Subject: [PATCH 0148/1346] make systests/databinding not fetch remote web-app_2_3.dtd (for 3.2.x and 3.1.x only) --- systests/databinding/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/systests/databinding/pom.xml b/systests/databinding/pom.xml index a3ac24b1dbe..3c5c009a7b6 100644 --- a/systests/databinding/pom.xml +++ b/systests/databinding/pom.xml @@ -184,6 +184,10 @@ cxf-rt-transports-http ${project.version} + + ${cxf.servlet-api.group} + ${cxf.servlet-api.artifact} + org.eclipse.jetty jetty-server From cc591f4a3b205a5dae58cadb79828d524a424513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Mon, 30 Nov 2015 16:13:17 +0100 Subject: [PATCH 0149/1346] Adding JAX-RS 2 extension for handling @BeanParam and @MatrixParam --- parent/pom.xml | 2 +- .../cxf/jaxrs/swagger/JaxRs2Extension.java | 151 ++++++++++++++++++ .../cxf/jaxrs/swagger/MatrixParameter.java | 28 ++++ .../io.swagger.jaxrs.ext.SwaggerExtension | 1 + .../cxf/jaxrs/swagger/SwaggerUtilsTest.java | 6 +- .../src/test/resources/swagger20.json | 15 +- 6 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java create mode 100644 rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java create mode 100644 rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension diff --git a/parent/pom.xml b/parent/pom.xml index 210c0b0128e..8ca9fdbb1b7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -144,7 +144,7 @@ 1.2.1 1.3.1.RELEASE spring-test - 1.3.12 + 1.3.13 1.5.4 1.7 4.4.1 diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java new file mode 100644 index 00000000000..3c8ae9546f7 --- /dev/null +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java @@ -0,0 +1,151 @@ +/** + * 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.jaxrs.swagger; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.ws.rs.BeanParam; +import javax.ws.rs.MatrixParam; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +import io.swagger.converter.ModelConverters; +import io.swagger.jaxrs.ext.AbstractSwaggerExtension; +import io.swagger.jaxrs.ext.SwaggerExtension; +import io.swagger.jaxrs.ext.SwaggerExtensions; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.RefProperty; +import io.swagger.models.properties.StringProperty; +import io.swagger.util.Json; +import io.swagger.util.ParameterProcessor; + +public class JaxRs2Extension extends AbstractSwaggerExtension { + + private final ObjectMapper mapper = Json.mapper(); + + @Override + public List extractParameters( + final List annotations, + final Type type, + final Set typesToSkip, + final Iterator chain) { + + if (shouldIgnoreType(type, typesToSkip)) { + return new ArrayList<>(); + } + + List parameters = new ArrayList<>(); + for (Annotation annotation : annotations) { + if (annotation instanceof MatrixParam) { + MatrixParam param = (MatrixParam) annotation; + MatrixParameter mp = new MatrixParameter().name(param.value()); + + Property schema = createProperty(type); + if (schema != null) { + mp.setProperty(schema); + } + parameters.add(mp); + } else if (annotation instanceof BeanParam) { + // Use Jackson's logic for processing Beans + final BeanDescription beanDesc = mapper.getSerializationConfig().introspect(constructType(type)); + final List properties = beanDesc.findProperties(); + + for (final BeanPropertyDefinition propDef : properties) { + final AnnotatedField field = propDef.getField(); + final AnnotatedMethod setter = propDef.getSetter(); + final List paramAnnotations = new ArrayList<>(); + final Iterator extensions = SwaggerExtensions.chain(); + Type paramType = null; + + // Gather the field's details + if (field != null) { + paramType = field.getGenericType(); + + for (final Annotation fieldAnnotation : field.annotations()) { + if (!paramAnnotations.contains(fieldAnnotation)) { + paramAnnotations.add(fieldAnnotation); + } + } + } + + // Gather the setter's details but only the ones we need + if (setter != null) { + // Do not set the param class/type from the setter if the values are already identified + if (paramType == null && setter.getGenericParameterTypes() != null) { + paramType = setter.getGenericParameterTypes()[0]; + } + + for (final Annotation fieldAnnotation : setter.annotations()) { + if (!paramAnnotations.contains(fieldAnnotation)) { + paramAnnotations.add(fieldAnnotation); + } + } + } + + // Re-process all Bean fields and let the default swagger-jaxrs processor do its thing + List extracted = + extensions.next().extractParameters(paramAnnotations, paramType, typesToSkip, extensions); + + // since downstream processors won't know how to introspect @BeanParam, process here + for (Parameter param : extracted) { + if (ParameterProcessor.applyAnnotations(null, param, paramType, paramAnnotations) != null) { + parameters.add(param); + } + } + } + } + } + + // Only call down to the other items in the chain if no parameters were produced + if (parameters.isEmpty()) { + parameters = super.extractParameters(annotations, type, typesToSkip, chain); + } + + return parameters; + } + + private Property createProperty(Type type) { + return enforcePrimitive(ModelConverters.getInstance().readAsProperty(type), 0); + } + + private Property enforcePrimitive(Property in, int level) { + if (in instanceof RefProperty) { + return new StringProperty(); + } + if (in instanceof ArrayProperty) { + if (level == 0) { + final ArrayProperty array = (ArrayProperty) in; + array.setItems(enforcePrimitive(array.getItems(), level + 1)); + } else { + return new StringProperty(); + } + } + return in; + } +} diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java new file mode 100644 index 00000000000..f1472c2e1b4 --- /dev/null +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java @@ -0,0 +1,28 @@ +/** + * 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.jaxrs.swagger; + +import io.swagger.models.parameters.AbstractSerializableParameter; + +public class MatrixParameter extends AbstractSerializableParameter { + + public MatrixParameter() { + super.setIn("matrix"); + } +} diff --git a/rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension b/rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension new file mode 100644 index 00000000000..9803485e96a --- /dev/null +++ b/rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension @@ -0,0 +1 @@ +org.apache.cxf.jaxrs.swagger.JaxRs2Extension diff --git a/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java b/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java index e9434e1dc29..5c3d6e9ca4c 100644 --- a/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java +++ b/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java @@ -57,7 +57,7 @@ public void testConvertSwagger20ToUserResource() { assertEquals("application/x-www-form-urlencoded", op.getConsumes()); assertEquals("application/json", op.getProduces()); - assertEquals(2, op.getParameters().size()); + assertEquals(3, op.getParameters().size()); Parameter param1 = op.getParameters().get(0); assertEquals("userName", param1.getName()); assertEquals(ParameterType.FORM, param1.getType()); @@ -66,5 +66,9 @@ public void testConvertSwagger20ToUserResource() { assertEquals("password", param2.getName()); assertEquals(ParameterType.FORM, param2.getType()); assertEquals(String.class, param2.getJavaType()); + Parameter param3 = op.getParameters().get(2); + assertEquals("type", param3.getName()); + assertEquals(ParameterType.MATRIX, param3.getType()); + assertEquals(String.class, param3.getJavaType()); } } diff --git a/rt/rs/description/src/test/resources/swagger20.json b/rt/rs/description/src/test/resources/swagger20.json index 44e7a324bba..f8f264df5db 100644 --- a/rt/rs/description/src/test/resources/swagger20.json +++ b/rt/rs/description/src/test/resources/swagger20.json @@ -23,7 +23,20 @@ "in": "formData", "name": "password", "type": "string" - } + }, + { + "name": "type", + "in": "matrix", + "required": true, + "type": "string", + "enum": [ + "PROPAGATION", + "NOTIFICATION", + "SCHEDULED", + "SYNCHRONIZATION", + "PUSH" + ] + } ] } } From ba17570e53cec4386f7c6fd40f075c6fbc1c3981 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 1 Dec 2015 12:30:20 +0000 Subject: [PATCH 0150/1346] Initial work toward linking Client and Consumer as suggested by Jan --- .../client/ClientCodeRequestFilter.java | 2 +- .../rs/security/oauth2/client/Consumer.java | 75 +++++++------------ .../rs/security/oauth2/client/Consumers.java | 40 ---------- .../oauth2/client/OAuthClientUtils.java | 8 +- .../rs/security/oidc/rp/IdTokenReader.java | 4 +- 5 files changed, 36 insertions(+), 93 deletions(-) delete mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumers.java diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java index 0aa2347916a..eb471805e0d 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/ClientCodeRequestFilter.java @@ -135,7 +135,7 @@ private Response createCodeResponse(ContainerRequestContext rc, UriInfo ui) { String redirectScope = redirectState != null ? redirectState.getFirst(OAuthConstants.SCOPE) : null; String theScope = redirectScope != null ? redirectScope : scopes; UriBuilder ub = OAuthClientUtils.getAuthorizationURIBuilder(authorizationServiceUri, - consumer.getKey(), + consumer.getClientId(), getAbsoluteRedirectUri(ui).toString(), theState, theScope); diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java index e592ec9c0cf..f58c28ad8d1 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumer.java @@ -18,80 +18,63 @@ */ package org.apache.cxf.rs.security.oauth2.client; -import java.util.HashSet; -import java.util.Set; - public class Consumer { - private String key; - private String secret; - private Set redirectURIs; - private String name; - private String description; - + private String clientId; + private String clientSecret; + public Consumer() { } - public Consumer(String key, String secret) { - this.setKey(key); - this.setSecret(secret); + public Consumer(String id, String secret) { + this.clientId = id; + this.clientSecret = secret; } + @Deprecated public String getKey() { - return key; + return getClientId(); } + @Deprecated public void setKey(String key) { - this.key = key; + setClientId(key); + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String id) { + this.clientId = id; } + @Deprecated public String getSecret() { - return secret; + return getClientSecret(); } + @Deprecated public void setSecret(String secret) { - this.secret = secret; + setClientSecret(secret); } - - public String getDescription() { - return description; + public String getClientSecret() { + return clientSecret; } - public void setDescription(String description) { - this.description = description; + public void setClientSecret(String secret) { + this.clientSecret = secret; } @Override public int hashCode() { - return key.hashCode(); + return clientId.hashCode(); } @Override public boolean equals(Object o) { - return o instanceof Consumer && key.equals(((Consumer)o).key); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Set getRedirectURIs() { - return redirectURIs; - } - - public void setRedirectURIs(Set redirectUri) { - this.redirectURIs = redirectUri; - } - - public boolean addRedirectURI(String redirectURI) { - if (this.redirectURIs == null) { - this.redirectURIs = new HashSet(); - } - return this.redirectURIs.add(redirectURI); + return o instanceof Consumer && clientId.equals(((Consumer)o).clientId); } + } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumers.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumers.java deleted file mode 100644 index 1e28de7a911..00000000000 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/Consumers.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * 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.client; - -import java.util.HashSet; -import java.util.Set; - -public class Consumers { - - private Set consumers = new HashSet(); - public Consumers() { - - } - public Consumers(Consumers consumers) { - this(consumers.getConsumers()); - } - public Consumers(Set consumers) { - this.consumers = consumers; - } - - public Set getConsumers() { - return consumers; - } -} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java index 17471f86741..9d19af91f9c 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/client/OAuthClientUtils.java @@ -282,21 +282,21 @@ public static ClientAccessToken getAccessToken(WebClient accessTokenService, } } if (consumer != null) { - boolean secretAvailable = !StringUtils.isEmpty(consumer.getSecret()); + boolean secretAvailable = !StringUtils.isEmpty(consumer.getClientSecret()); if (setAuthorizationHeader && secretAvailable) { StringBuilder sb = new StringBuilder(); sb.append("Basic "); try { - String data = consumer.getKey() + ":" + consumer.getSecret(); + String data = consumer.getClientId() + ":" + consumer.getClientSecret(); sb.append(Base64Utility.encode(data.getBytes(StandardCharsets.UTF_8))); } catch (Exception ex) { throw new ProcessingException(ex); } accessTokenService.replaceHeader("Authorization", sb.toString()); } else { - form.param(OAuthConstants.CLIENT_ID, consumer.getKey()); + form.param(OAuthConstants.CLIENT_ID, consumer.getClientId()); if (secretAvailable) { - form.param(OAuthConstants.CLIENT_SECRET, consumer.getSecret()); + form.param(OAuthConstants.CLIENT_SECRET, consumer.getClientSecret()); } } } else { diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java index 9072add7daf..4c9071c0342 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java @@ -41,8 +41,8 @@ public JwtToken getIdJwtToken(ClientAccessToken at, Consumer client) { return jwt; } public JwtToken getIdJwtToken(String idJwtToken, Consumer client) { - JwtToken jwt = getJwtToken(idJwtToken, client.getSecret()); - validateJwtClaims(jwt.getClaims(), client.getKey(), true); + JwtToken jwt = getJwtToken(idJwtToken, client.getClientSecret()); + validateJwtClaims(jwt.getClaims(), client.getClientId(), true); return jwt; } private IdToken getIdTokenFromJwt(JwtToken jwt) { From 82c4bff5276cc32490507fba4cb9d18539d41907 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 1 Dec 2015 12:11:12 +0000 Subject: [PATCH 0151/1346] Avoid some NPEs --- .../cxf/rs/security/jose/common/KeyManagementUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java index b18295a4579..79b1aede7a9 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java @@ -236,11 +236,14 @@ public static KeyStore loadPersistKeyStore(Message m, Properties props) { LOG.warning("No keystore file has been configured"); throw new JoseException("No keystore file has been configured"); } - keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE)); + if (m != null) { + keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE)); + } } if (keyStore == null) { - keyStore = loadKeyStore(props, m.getExchange().getBus()); + Bus bus = m != null ? m.getExchange().getBus() : null; + keyStore = loadKeyStore(props, bus); if (m != null) { m.getExchange().put((String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE), keyStore); } From c66b1a18100ac752e8a2fff712c01e6c682ffcd7 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 1 Dec 2015 14:02:22 +0000 Subject: [PATCH 0152/1346] Adding a filter to authenticate JWT Tokens for the JWT Bearer Client Authentication case --- .../jose/jaxrs/JwtAuthenticationFilter.java | 10 +- .../cxf/rs/security/jose/jwt/JwtUtils.java | 12 ++ rt/rs/security/oauth-parent/oauth2/pom.xml | 2 +- .../oauth2/grants/jwt/AbstractJwtHandler.java | 22 ++-- .../grants/jwt/JwtBearerAuthHandler.java | 111 ++++++++++++++++++ 5 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerAuthHandler.java diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java index e52897c3808..2dc6095d4d4 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java @@ -96,15 +96,7 @@ public void setExpectedAuthScheme(String expectedAuthScheme) { @Override protected void validateToken(JwtToken jwt) { - // If we have no issued time then we need to have an expiry - boolean expiredRequired = jwt.getClaims().getIssuedAt() == null; - JwtUtils.validateJwtExpiry(jwt.getClaims(), clockOffset, expiredRequired); - - JwtUtils.validateJwtNotBefore(jwt.getClaims(), clockOffset, false); - - // If we have no expiry then we must have an issued at - boolean issuedAtRequired = jwt.getClaims().getExpiryTime() == null; - JwtUtils.validateJwtIssuedAt(jwt.getClaims(), ttl, clockOffset, issuedAtRequired); + JwtUtils.validateTokenClaims(jwt.getClaims(), ttl, clockOffset); } public int getClockOffset() { diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java index 9f1c1d68d0b..fa6989a8692 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java @@ -110,4 +110,16 @@ public static void validateJwtIssuedAt(JwtClaims claims, int timeToLive, int clo } } + public static void validateTokenClaims(JwtClaims claims, int timeToLive, int clockOffset) { + // If we have no issued time then we need to have an expiry + boolean expiredRequired = claims.getIssuedAt() == null; + validateJwtExpiry(claims, clockOffset, expiredRequired); + + validateJwtNotBefore(claims, clockOffset, false); + + // If we have no expiry then we must have an issued at + boolean issuedAtRequired = claims.getExpiryTime() == null; + validateJwtIssuedAt(claims, timeToLive, clockOffset, issuedAtRequired); + } + } diff --git a/rt/rs/security/oauth-parent/oauth2/pom.xml b/rt/rs/security/oauth-parent/oauth2/pom.xml index 7886387429b..bdea3fe5f5a 100644 --- a/rt/rs/security/oauth-parent/oauth2/pom.xml +++ b/rt/rs/security/oauth-parent/oauth2/pom.xml @@ -44,7 +44,7 @@ org.apache.cxf - cxf-rt-rs-security-jose + cxf-rt-rs-security-jose-jaxrs ${project.version} diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java index 4f966c212ac..b8c626783b0 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java @@ -25,6 +25,7 @@ import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; import org.apache.cxf.rs.security.jose.jws.JwsUtils; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.rs.security.jose.jwt.JwtUtils; import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rs.security.oauth2.grants.AbstractGrantHandler; @@ -53,19 +54,16 @@ protected void validateSignature(JwsHeaders headers, String unsignedText, byte[] } protected void validateClaims(Client client, JwtClaims claims) { + JwtUtils.validateTokenClaims(claims, ttl, clockOffset); + validateIssuer(claims.getIssuer()); validateSubject(client, claims.getSubject()); validateAudience(client, claims.getAudience()); - // If we have no issued time then we need to have an expiry - boolean expiredRequired = claims.getIssuedAt() == null; - JwtUtils.validateJwtExpiry(claims, clockOffset, expiredRequired); - - JwtUtils.validateJwtNotBefore(claims, clockOffset, false); - - // If we have no expiry then we must have an issued at - boolean issuedAtRequired = claims.getExpiryTime() == null; - JwtUtils.validateJwtIssuedAt(claims, ttl, clockOffset, issuedAtRequired); + // We must have an Expiry + if (claims.getClaim(JwtConstants.CLAIM_EXPIRY) == null) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); + } } protected void validateIssuer(String issuer) { @@ -75,10 +73,12 @@ protected void validateIssuer(String issuer) { } protected void validateSubject(Client client, String subject) { - //TODO + // We must have a Subject + if (subject == null) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); + } } protected void validateAudience(Client client, String audience) { - //TODO } public void setSupportedIssuers(Set supportedIssuers) { this.supportedIssuers = supportedIssuers; diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerAuthHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerAuthHandler.java new file mode 100644 index 00000000000..f8c4ee54dbb --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/JwtBearerAuthHandler.java @@ -0,0 +1,111 @@ +/** + * 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.grants.jwt; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.Form; +import javax.ws.rs.core.MultivaluedMap; + +import org.apache.cxf.jaxrs.provider.FormEncodingProvider; +import org.apache.cxf.jaxrs.utils.ExceptionUtils; +import org.apache.cxf.jaxrs.utils.FormUtils; +import org.apache.cxf.jaxrs.utils.HttpUtils; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.rs.security.jose.jaxrs.JwtAuthenticationFilter; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; +import org.apache.cxf.security.SecurityContext; + +public class JwtBearerAuthHandler extends JwtAuthenticationFilter { + private FormEncodingProvider
provider = new FormEncodingProvider(true); + + public JwtBearerAuthHandler() { + } + + @Override + public void filter(ContainerRequestContext context) { + Message message = JAXRSUtils.getCurrentMessage(); + Form form = readFormData(message); + MultivaluedMap formData = form.asMap(); + String assertionType = formData.getFirst(Constants.CLIENT_AUTH_ASSERTION_TYPE); + String decodedAssertionType = assertionType != null ? HttpUtils.urlDecode(assertionType) : null; + if (decodedAssertionType == null || !Constants.CLIENT_AUTH_JWT_BEARER.equals(decodedAssertionType)) { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } + + String assertion = formData.getFirst(Constants.CLIENT_AUTH_ASSERTION_PARAM); + JwtToken token = super.getJwtToken(assertion); + + String clientId = formData.getFirst(OAuthConstants.CLIENT_ID); + String subjectName = (String)token.getClaim(JwtConstants.CLAIM_SUBJECT); + if (clientId != null && !clientId.equals(subjectName)) { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } + message.put(OAuthConstants.CLIENT_ID, subjectName); + + formData.remove(OAuthConstants.CLIENT_ID); + formData.remove(Constants.CLIENT_AUTH_ASSERTION_PARAM); + formData.remove(Constants.CLIENT_AUTH_ASSERTION_TYPE); + + SecurityContext securityContext = configureSecurityContext(token); + if (securityContext != null) { + JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext); + } + + // restore input stream + try { + FormUtils.restoreForm(provider, form, message); + } catch (Exception ex) { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } + } + + private Form readFormData(Message message) { + try { + return FormUtils.readForm(provider, message); + } catch (Exception ex) { + throw ExceptionUtils.toNotAuthorizedException(null, null); + } + } + + @Override + protected void validateToken(JwtToken jwt) { + super.validateToken(jwt); + + // We must have an issuer + if (jwt.getClaim(JwtConstants.CLAIM_ISSUER) == null) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); + } + + // We must have a Subject + if (jwt.getClaim(JwtConstants.CLAIM_SUBJECT) == null) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); + } + + // We must have an Expiry + if (jwt.getClaim(JwtConstants.CLAIM_EXPIRY) == null) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); + } + } + +} From 088dcdf8c7a3626adf650e80b98a9efd51d48653 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 1 Dec 2015 16:30:33 +0000 Subject: [PATCH 0153/1346] Reordering the way request dispatcher provider checks resources to ensure the more dynamic settings are prefferred --- .../provider/RequestDispatcherProvider.java | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/RequestDispatcherProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/RequestDispatcherProvider.java index 4422d817036..a2d239fc7dd 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/RequestDispatcherProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/RequestDispatcherProvider.java @@ -238,49 +238,57 @@ private void logRedirection(String path, String attributeName, Object o) { } String getResourcePath(Class cls, Object o) { - if (useClassNames) { - return getClassResourceName(cls); + String currentResourcePath = getPathFromMessageContext(); + if (currentResourcePath != null) { + return currentResourcePath; } - String name = cls.getName(); - if (cls.isEnum()) { - String enumResource = enumResources.get(o); - if (enumResource != null) { - return enumResource; + if (!resourcePaths.isEmpty()) { + + String path = getRequestPath(); + for (Map.Entry entry : resourcePaths.entrySet()) { + if (path.endsWith(entry.getKey())) { + return entry.getValue(); + } } - name += "." + o.toString(); - } - - String clsResourcePath = classResources.get(name); - if (clsResourcePath != null) { - return clsResourcePath; } - if (resourcePath != null) { - return resourcePath; - } - String path = getRequestPath(); - for (Map.Entry entry : resourcePaths.entrySet()) { - if (path.endsWith(entry.getKey())) { - return entry.getValue(); + if (!enumResources.isEmpty() || !classResources.isEmpty()) { + String name = cls.getName(); + if (cls.isEnum()) { + String enumResource = enumResources.get(o); + if (enumResource != null) { + return enumResource; + } + name += "." + o.toString(); + } + + String clsResourcePath = classResources.get(name); + if (clsResourcePath != null) { + return clsResourcePath; } } - return getPathFromMessageContext(); + if (useClassNames) { + return getClassResourceName(cls); + } + return resourcePath; } private String getPathFromMessageContext() { - Object resourcePathProp = (String)mc.get(MESSAGE_RESOURCE_PATH_PROPERTY); - if (resourcePathProp != null) { - StringBuilder sb = new StringBuilder(); - if (locationPrefix != null) { - sb.append(locationPrefix); - } - sb.append(resourcePathProp.toString()); - if (resourceExtension != null) { - sb.append(resourceExtension); + if (mc != null) { + Object resourcePathProp = (String)mc.get(MESSAGE_RESOURCE_PATH_PROPERTY); + if (resourcePathProp != null) { + StringBuilder sb = new StringBuilder(); + if (locationPrefix != null) { + sb.append(locationPrefix); + } + sb.append(resourcePathProp.toString()); + if (resourceExtension != null) { + sb.append(resourceExtension); + } + return sb.toString(); } - return sb.toString(); } return null; } From 924e6646acc21451c048d72056e90ff42b9c49c0 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 1 Dec 2015 17:08:01 +0000 Subject: [PATCH 0154/1346] Updating oidc demos --- .../basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml | 2 +- .../big_query/src/main/webapp/WEB-INF/applicationContext.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml b/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml index 6113a9be4d3..91f6deaf226 100644 --- a/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml +++ b/distribution/src/main/release/samples/jax_rs/basic_oidc/src/main/webapp/WEB-INF/applicationContext.xml @@ -112,7 +112,7 @@ - + diff --git a/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml b/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml index 71717977331..d8f9ef403a4 100644 --- a/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml +++ b/distribution/src/main/release/samples/jax_rs/big_query/src/main/webapp/WEB-INF/applicationContext.xml @@ -189,8 +189,8 @@ - - + + From 741f15d967c61f4b968aa0d5832699f1ccec7570 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 1 Dec 2015 21:15:16 +0000 Subject: [PATCH 0155/1346] If a named principal is available then keep the existing sec context --- .../security/jose/jaxrs/JwsContainerRequestFilter.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java index 003e674d936..44ba01a8b9f 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java @@ -60,9 +60,12 @@ public void filter(ContainerRequestContext context) throws IOException { context.getHeaders().putSingle("Content-Type", ct); } - SecurityContext securityContext = configureSecurityContext(theSigVerifier); - if (securityContext != null) { - JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext); + Principal currentPrincipal = context.getSecurityContext().getUserPrincipal(); + if (currentPrincipal != null && currentPrincipal.getName() != null) { + SecurityContext securityContext = configureSecurityContext(theSigVerifier); + if (securityContext != null) { + JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext); + } } } From 6a088ce378e8fd558cb82ceb698f66bd5819e437 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 2 Dec 2015 10:11:06 +0000 Subject: [PATCH 0156/1346] Fixing setting the sec. context --- .../cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java index 44ba01a8b9f..a238384b00f 100644 --- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java +++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsContainerRequestFilter.java @@ -61,7 +61,7 @@ public void filter(ContainerRequestContext context) throws IOException { } Principal currentPrincipal = context.getSecurityContext().getUserPrincipal(); - if (currentPrincipal != null && currentPrincipal.getName() != null) { + if (currentPrincipal == null || currentPrincipal.getName() == null) { SecurityContext securityContext = configureSecurityContext(theSigVerifier); if (securityContext != null) { JAXRSUtils.getCurrentMessage().put(SecurityContext.class, securityContext); From db9b8a631e9597d8fa0ff5d1f526a29d00f16b00 Mon Sep 17 00:00:00 2001 From: Akitoshi Yoshida Date: Wed, 2 Dec 2015 11:58:58 +0100 Subject: [PATCH 0157/1346] [CXF-6699] DefaultAddress not set in HTTPConduit class --- .../main/java/org/apache/cxf/transport/http/HTTPConduit.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java index bb2142476c4..9d4635b125f 100644 --- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java +++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java @@ -682,7 +682,8 @@ private Address setupAddress(Message message) throws URISyntaxException { return defaultAddress; } if (defaultAddress != null) { - message.put(Message.ENDPOINT_ADDRESS, defaultAddress.getString()); + result = defaultAddress.getString(); + message.put(Message.ENDPOINT_ADDRESS, result); } } From 7fb697a7f54997138295a5f01c51f6640376f929 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 2 Dec 2015 10:50:13 +0000 Subject: [PATCH 0158/1346] A few minor changes to the JWT Bearer Handler --- .../rs/security/oauth2/grants/jwt/AbstractJwtHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java index b8c626783b0..01773239bfa 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/jwt/AbstractJwtHandler.java @@ -47,7 +47,7 @@ protected AbstractJwtHandler(List grants) { } protected void validateSignature(JwsHeaders headers, String unsignedText, byte[] signature) { - JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(); + JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(headers); if (!theSigVerifier.verify(headers, unsignedText, signature)) { throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); } @@ -67,7 +67,7 @@ protected void validateClaims(Client client, JwtClaims claims) { } protected void validateIssuer(String issuer) { - if (issuer == null || !supportedIssuers.contains(issuer)) { + if (issuer == null || (supportedIssuers != null && !supportedIssuers.contains(issuer))) { throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); } } @@ -87,11 +87,11 @@ public void setSupportedIssuers(Set supportedIssuers) { public void setJwsVerifier(JwsSignatureVerifier jwsVerifier) { this.jwsVerifier = jwsVerifier; } - protected JwsSignatureVerifier getInitializedSigVerifier() { + protected JwsSignatureVerifier getInitializedSigVerifier(JwsHeaders headers) { if (jwsVerifier != null) { return jwsVerifier; } - return JwsUtils.loadSignatureVerifier(true); + return JwsUtils.loadSignatureVerifier(headers, true); } public int getTtl() { From 4685f06871bf35cee184d020fed025ae9491cb02 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Wed, 2 Dec 2015 12:44:05 +0000 Subject: [PATCH 0159/1346] Disabling some tests that continually fail on Jenkins --- .../test/java/org/apache/cxf/javascript/JsHttpRequestTest.java | 3 ++- .../org/apache/cxf/systest/http_jetty/EngineLifecycleTest.java | 2 ++ .../java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rt/javascript/javascript-tests/src/test/java/org/apache/cxf/javascript/JsHttpRequestTest.java b/rt/javascript/javascript-tests/src/test/java/org/apache/cxf/javascript/JsHttpRequestTest.java index c29f22de44f..b2055a03c7a 100644 --- a/rt/javascript/javascript-tests/src/test/java/org/apache/cxf/javascript/JsHttpRequestTest.java +++ b/rt/javascript/javascript-tests/src/test/java/org/apache/cxf/javascript/JsHttpRequestTest.java @@ -43,8 +43,9 @@ import org.springframework.context.support.GenericApplicationContext; /** - * + * This test is ignored by default as it is continually failing on Jenkins. */ +@org.junit.Ignore public class JsHttpRequestTest extends AbstractCXFSpringTest { // shadow declaration from base class. diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http_jetty/EngineLifecycleTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/http_jetty/EngineLifecycleTest.java index 81448d53df5..ae45f512abc 100644 --- a/systests/transports/src/test/java/org/apache/cxf/systest/http_jetty/EngineLifecycleTest.java +++ b/systests/transports/src/test/java/org/apache/cxf/systest/http_jetty/EngineLifecycleTest.java @@ -58,7 +58,9 @@ /** * This class tests starting up and shutting down the embedded server when there * is extra jetty configuration. + * This test is ignored by default as it is continually failing on Jenkins. */ +@org.junit.Ignore public class EngineLifecycleTest extends Assert { private static final String PORT1 = TestUtil.getPortNumber(EngineLifecycleTest.class, 1); private static final String PORT2 = TestUtil.getPortNumber(EngineLifecycleTest.class, 2); diff --git a/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java b/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java index ef0fa797779..30d30314626 100644 --- a/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java +++ b/tools/wsdlto/test/src/test/java/org/apache/cxf/tools/wsdlto/jaxws/CodeGenBugTest.java @@ -485,7 +485,9 @@ public void testDefatultNsMapExclude() throws Exception { assertFalse(orginal.exists()); } + // @Ignore'd due to continually failing on Jenkins @Test + @org.junit.Ignore public void testHelloWorldExternalBindingFile() throws Exception { Server server = new Server(0); From 8c02d3c817c730851ebe748043daaec10190b125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Wed, 2 Dec 2015 15:37:20 +0100 Subject: [PATCH 0160/1346] Do not initialize if no swagger-jaxrs classes are found --- .../jaxrs/swagger/AbstractSwaggerFeature.java | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java index 87dfdfe75d6..08d2e16a8cf 100644 --- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/AbstractSwaggerFeature.java @@ -25,6 +25,13 @@ import org.apache.cxf.jaxrs.model.AbstractResourceInfo; public abstract class AbstractSwaggerFeature extends AbstractFeature { + + private static final boolean SWAGGER_JAXRS_AVAILABLE; + + static { + SWAGGER_JAXRS_AVAILABLE = isSwaggerJaxRsAvailable(); + } + protected boolean scan = true; protected boolean runAsFilter; private String resourcePackage; @@ -39,13 +46,24 @@ public abstract class AbstractSwaggerFeature extends AbstractFeature { private String termsOfServiceUrl; private String filterClass; + private static boolean isSwaggerJaxRsAvailable() { + try { + Class.forName("io.swagger.jaxrs.DefaultParameterExtension"); + return true; + } catch (Throwable ex) { + return false; + } + } + @Override public void initialize(Server server, Bus bus) { - calculateDefaultResourcePackage(server); - calculateDefaultBasePath(server); - addSwaggerResource(server); - - initializeProvider(server.getEndpoint(), bus); + if (SWAGGER_JAXRS_AVAILABLE) { + calculateDefaultResourcePackage(server); + calculateDefaultBasePath(server); + addSwaggerResource(server); + + initializeProvider(server.getEndpoint(), bus); + } } protected abstract void addSwaggerResource(Server server); From e4c456743a046f37ce9623529635218be7f13e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Wed, 2 Dec 2015 15:38:13 +0100 Subject: [PATCH 0161/1346] Support multiple URLs for javadocs --- .../cxf/jaxrs/model/doc/JavaDocProvider.java | 49 +++++-- .../cxf/jaxrs/model/wadl/WadlGenerator.java | 138 +++++++++--------- .../cxf/jaxrs/swagger/Swagger2Feature.java | 12 +- .../jaxrs/swagger/Swagger2Serializers.java | 12 +- 4 files changed, 119 insertions(+), 92 deletions(-) diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java index ae81234d38b..5e702fe579d 100644 --- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/doc/JavaDocProvider.java @@ -44,22 +44,35 @@ public class JavaDocProvider implements DocumentationProvider { public static final double JAVA_VERSION_18 = 1.8D; private ClassLoader javaDocLoader; - private ConcurrentHashMap docs = new ConcurrentHashMap(); + private final ConcurrentHashMap docs = new ConcurrentHashMap<>(); private double javaDocsBuiltByVersion = JAVA_VERSION; - public JavaDocProvider(URL javaDocUrl) { - if (javaDocUrl == null) { - throw new IllegalArgumentException("URL is null"); + public JavaDocProvider(URL... javaDocUrls) { + if (javaDocUrls == null) { + throw new IllegalArgumentException("URL are null"); } - javaDocLoader = new URLClassLoader(new URL[]{javaDocUrl}); + + javaDocLoader = new URLClassLoader(javaDocUrls); } public JavaDocProvider(String path) throws Exception { this(BusFactory.getDefaultBus(), path); } - public JavaDocProvider(Bus bus, String path) throws Exception { - this(ResourceUtils.getResourceURL(path, bus)); + public JavaDocProvider(String... paths) throws Exception { + this(BusFactory.getDefaultBus(), paths == null ? null : paths); + } + + public JavaDocProvider(Bus bus, String... paths) throws Exception { + if (paths == null) { + throw new IllegalArgumentException("paths are null"); + } + + URL[] javaDocUrls = new URL[paths.length]; + for (int i = 0; i < paths.length; i++) { + javaDocUrls[i] = ResourceUtils.getResourceURL(paths[i], bus); + } + javaDocLoader = new URLClassLoader(javaDocUrls); } private static double getVersion() { @@ -71,6 +84,7 @@ private static double getVersion() { } } + @Override public String getClassDoc(ClassResourceInfo cri) { try { ClassDocs doc = getClassDocInternal(cri.getServiceClass()); @@ -84,6 +98,7 @@ public String getClassDoc(ClassResourceInfo cri) { return null; } + @Override public String getMethodDoc(OperationResourceInfo ori) { try { MethodDocs doc = getOperationDocInternal(ori); @@ -97,6 +112,7 @@ public String getMethodDoc(OperationResourceInfo ori) { return null; } + @Override public String getMethodResponseDoc(OperationResourceInfo ori) { try { MethodDocs doc = getOperationDocInternal(ori); @@ -110,6 +126,7 @@ public String getMethodResponseDoc(OperationResourceInfo ori) { return null; } + @Override public String getMethodParameterDoc(OperationResourceInfo ori, int paramIndex) { try { MethodDocs doc = getOperationDocInternal(ori); @@ -211,7 +228,7 @@ private MethodDocs getOperationDocInternal(OperationResourceInfo ori) throws Exc String operInfoTag = getOperInfoTag(); String operInfo = getJavaDocText(operDoc, operInfoTag, operLink, 0); String responseInfo = null; - List paramDocs = new LinkedList(); + List paramDocs = new LinkedList<>(); if (!StringUtils.isEmpty(operInfo)) { int returnsIndex = operDoc.indexOf("Returns:", operLink.length()); int nextOpIndex = operDoc.indexOf(operLink); @@ -228,7 +245,7 @@ private MethodDocs getOperationDocInternal(OperationResourceInfo ori) throws Exc int codeIndex = paramString.indexOf(codeTag); while (codeIndex != -1) { - int next = paramString.indexOf("<", codeIndex + 7); + int next = paramString.indexOf('<', codeIndex + 7); if (next == -1) { next = paramString.length(); } @@ -261,7 +278,7 @@ private String getJavaDocText(String doc, String tag, String notAfterTag, int in if (tagIndex != -1) { int notAfterIndex = doc.indexOf(notAfterTag, index); if (notAfterIndex == -1 || notAfterIndex > tagIndex) { - int nextIndex = doc.indexOf("<", tagIndex + tag.length()); + int nextIndex = doc.indexOf('<', tagIndex + tag.length()); if (nextIndex != -1) { return doc.substring(tagIndex + tag.length(), nextIndex).trim(); } @@ -313,9 +330,9 @@ public void setJavaDocsBuiltByVersion(String version) { } private static class ClassDocs { - private String classDoc; - private String classInfo; - private ConcurrentHashMap mdocs = new ConcurrentHashMap(); + private final String classDoc; + private final String classInfo; + private final ConcurrentHashMap mdocs = new ConcurrentHashMap<>(); ClassDocs(String classDoc, String classInfo) { this.classDoc = classDoc; this.classInfo = classInfo; @@ -339,9 +356,9 @@ public void addMethodDocs(Method method, MethodDocs doc) { } private static class MethodDocs { - private String methodInfo; - private List paramInfo = new LinkedList(); - private String responseInfo; + private final String methodInfo; + private final List paramInfo; + private final String responseInfo; MethodDocs(String methodInfo, List paramInfo, String responseInfo) { this.methodInfo = methodInfo; this.paramInfo = paramInfo; diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java index dd044fe6c87..29b4467463d 100644 --- a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/model/wadl/WadlGenerator.java @@ -30,6 +30,7 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -146,7 +147,7 @@ public class WadlGenerator implements ContainerRequestFilter { private static final String DEFAULT_NS_PREFIX = "prefix"; private static final Map> PARAMETER_TYPE_MAP; static { - PARAMETER_TYPE_MAP = new HashMap>(); + PARAMETER_TYPE_MAP = new HashMap<>(); PARAMETER_TYPE_MAP.put(ParameterType.FORM, FormParam.class); PARAMETER_TYPE_MAP.put(ParameterType.QUERY, QueryParam.class); PARAMETER_TYPE_MAP.put(ParameterType.HEADER, HeaderParam.class); @@ -168,8 +169,7 @@ public class WadlGenerator implements ContainerRequestFilter { private boolean checkAbsolutePathSlash; private boolean keepRelativeDocLinks; private boolean usePathParamsToCompareOperations = true; - - + private boolean ignoreMessageWriters = true; private boolean ignoreRequests; private boolean convertResourcesToDOM = true; @@ -178,21 +178,19 @@ public class WadlGenerator implements ContainerRequestFilter { private List externalSchemaLinks; private Map> externalQnamesMap; - private ConcurrentHashMap docLocationMap = new ConcurrentHashMap(); + private final ConcurrentHashMap docLocationMap = new ConcurrentHashMap<>(); private ElementQNameResolver resolver; private List privateAddresses; private String applicationTitle; private String nsPrefix = DEFAULT_NS_PREFIX; private MediaType defaultWadlResponseMediaType = MediaType.APPLICATION_XML_TYPE; - private MediaType defaultRepMediaType = MediaType.WILDCARD_TYPE; + private final MediaType defaultRepMediaType = MediaType.WILDCARD_TYPE; private String stylesheetReference; private boolean applyStylesheetLocally; private Bus bus; - private List docProviders = new LinkedList(); - private ResourceIdGenerator idGenerator; - - + private final List docProviders = new LinkedList(); + private ResourceIdGenerator idGenerator; public WadlGenerator() { } @@ -201,8 +199,8 @@ public WadlGenerator(Bus bus) { this.bus = bus; } + @Override public void filter(ContainerRequestContext context) { - Message m = JAXRSUtils.getCurrentMessage(); doFilter(context, m); } @@ -270,7 +268,7 @@ public StringBuilder generateWADL(String baseURI, UriInfo ui) { StringBuilder sbMain = new StringBuilder(); if (!isJson && stylesheetReference != null && !applyStylesheetLocally) { - sbMain.append(""); + sbMain.append(""); } sbMain.append(">(allTypes), null, null); + jaxbContext = ResourceUtils.createJaxbContext(new HashSet<>(allTypes), null, null); if (jaxbContext == null) { LOG.warning("JAXB Context is null: possibly due to one of input classes being not accepted"); } @@ -305,8 +303,8 @@ public StringBuilder generateWADL(String baseURI, ElementQNameResolver qnameResolver = schemaWriter == null ? null : createElementQNameResolver(jaxbContext); - Map, QName> clsMap = new IdentityHashMap, QName>(); - Set visitedResources = new LinkedHashSet(); + Map, QName> clsMap = new IdentityHashMap<>(); + Set visitedResources = new LinkedHashSet<>(); for (ClassResourceInfo cri : cris) { startResourceTag(sbResources, cri, cri.getURITemplate().getValue()); @@ -366,7 +364,7 @@ protected void handleGrammars(StringBuilder sbApp, StringBuilder sbGrammars, Sch return; } - Map map = new HashMap(); + Map map = new HashMap<>(); for (QName qname : clsMap.values()) { map.put(qname.getPrefix(), qname.getNamespaceURI()); } @@ -424,7 +422,7 @@ protected void handleResource(StringBuilder sb, Set> jaxbTypes, } private Map getClassParameters(ClassResourceInfo cri) { - Map classParams = new LinkedHashMap(); + Map classParams = new LinkedHashMap<>(); List paramMethods = cri.getParameterMethods(); for (Method m : paramMethods) { classParams.put(ResourceUtils.getParameter(0, m.getAnnotations(), m.getParameterTypes()[0]), m); @@ -446,7 +444,7 @@ protected void startResourceTag(StringBuilder sb, ClassResourceInfo cri, String } protected String getPath(String path) { - String thePath = null; + String thePath; if (ignoreForwardSlash && path.startsWith("/") && path.length() > 0) { thePath = path.substring(1); } else { @@ -460,7 +458,7 @@ private void checkXmlSeeAlso(ResourceTypes resourceTypes) { if (!this.useJaxbContextForQnames) { return; } - List> extraClasses = new LinkedList>(); + List> extraClasses = new LinkedList<>(); for (Class cls : resourceTypes.getAllTypes().keySet()) { if (!isXmlRoot(cls) || Modifier.isAbstract(cls.getModifiers())) { XmlSeeAlso seeAlsoAnn = cls.getAnnotation(XmlSeeAlso.class); @@ -579,7 +577,7 @@ protected boolean handleOperation(StringBuilder sb, Set> jaxbTypes, if (!handleDocs(anns, sb, DocTarget.METHOD, true, isJson)) { handleOperJavaDocs(ori, sb); } - if (getMethod(ori).getParameterTypes().length != 0 || classParams.size() != 0) { + if (getMethod(ori).getParameterTypes().length != 0 || !classParams.isEmpty()) { startMethodRequestTag(sb, ori); handleDocs(anns, sb, DocTarget.REQUEST, false, isJson); @@ -708,7 +706,7 @@ protected void doHandleClassParams(OperationResourceInfo ori, Map params, boolean isJson, ParameterType... pType) { - Set pTypes = new LinkedHashSet(Arrays.asList(pType)); + Set pTypes = new LinkedHashSet<>(Arrays.asList(pType)); for (Map.Entry entry : params.entrySet()) { Parameter pm = entry.getKey(); Object obj = entry.getValue(); @@ -803,7 +801,7 @@ private void writeParam(StringBuilder sb, Parameter pm, OperationResourceInfo or method.getParameterAnnotations()[pm.getIndex()], isJson); } else { - List> parentBeanClasses = new LinkedList>(); + List> parentBeanClasses = new LinkedList<>(); parentBeanClasses.add(type); doWriteBeanParam(ori, sb, type, pm, null, parentBeanClasses, isJson); parentBeanClasses.remove(type); @@ -940,12 +938,12 @@ protected void doWriteParam(OperationResourceInfo ori, private void setEnumOptions(StringBuilder sb, Class enumClass) { try { - Method m = enumClass.getMethod("values", new Class[] {}); + Method m = enumClass.getMethod("values", new Class[] {}); Object[] values = (Object[])m.invoke(null, new Object[] {}); - m = enumClass.getMethod("toString", new Class[] {}); + m = enumClass.getMethod("toString", new Class[] {}); for (Object o : values) { String str = (String)m.invoke(o, new Object[] {}); - sb.append("