/** * 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.rest; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; import javax.security.auth.callback.CallbackHandler; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBElement; import javax.xml.transform.dom.DOMSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBusFactory; import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.common.util.CompressionUtils; import org.apache.cxf.jaxrs.client.WebClient; 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; import org.apache.cxf.rt.security.claims.Claim; import org.apache.cxf.rt.security.claims.ClaimCollection; import org.apache.cxf.rt.security.saml.utils.SAMLUtils; import org.apache.cxf.staxutils.StaxUtils; import org.apache.cxf.staxutils.W3CDOMStreamWriter; import org.apache.cxf.systest.sts.common.SecurityTestUtil; import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; 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.trust.STSUtils; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.saml.OpenSAMLUtil; import org.apache.wss4j.common.saml.SAMLKeyInfo; import org.apache.wss4j.common.saml.SamlAssertionWrapper; import org.apache.wss4j.common.util.Loader; import org.apache.wss4j.dom.WSDocInfo; import org.apache.wss4j.dom.engine.WSSecurityEngineResult; import org.apache.wss4j.dom.handler.RequestData; import org.apache.wss4j.dom.processor.Processor; import org.apache.wss4j.dom.processor.SAMLTokenProcessor; import org.junit.Assert; import org.junit.BeforeClass; /** * Some tests for the REST interface of the CXF STS. */ public class STSRESTTest extends AbstractBusClientServerTestBase { static final String STSPORT = allocatePort(STSRESTServer.class); private static final String SAML1_TOKEN_TYPE = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"; private static final String SAML2_TOKEN_TYPE = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"; private static final String JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt"; private static final String SYMMETRIC_KEY_KEYTYPE = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey"; private static final String PUBLIC_KEY_KEYTYPE = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey"; private static final String BEARER_KEYTYPE = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer"; 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(STSRESTServer.class, true) ); } @org.junit.AfterClass public static void cleanup() throws Exception { SecurityTestUtil.cleanup(); stopAllServers(); } @org.junit.Test public void testIssueSAML2Token() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); bus.shutdown(true); } @org.junit.Test public void testIssueSAML1Token() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml1.1"); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); assertTrue(assertion.isSigned()); bus.shutdown(true); } @org.junit.Test public void testIssueSymmetricKeySaml1() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml1.1"); client.query("keyType", SYMMETRIC_KEY_KEYTYPE); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); assertTrue(assertion.isSigned()); List<String> methods = assertion.getConfirmationMethods(); String confirmMethod = null; if (methods != null && !methods.isEmpty()) { confirmMethod = methods.get(0); } assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)); SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo(); assertTrue(subjectKeyInfo.getSecret() != null); bus.shutdown(true); } @org.junit.Test public void testIssueSymmetricKeySaml1ShortKeyType() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml1.1"); client.query("keyType", "SymmetricKey"); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); assertTrue(assertion.isSigned()); List<String> methods = assertion.getConfirmationMethods(); String confirmMethod = null; if (methods != null && !methods.isEmpty()) { confirmMethod = methods.get(0); } assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)); SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo(); assertTrue(subjectKeyInfo.getSecret() != null); bus.shutdown(true); } @org.junit.Test public void testIssuePublicKeySAML2Token() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); client.query("keyType", PUBLIC_KEY_KEYTYPE); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); List<String> methods = assertion.getConfirmationMethods(); String confirmMethod = null; if (methods != null && !methods.isEmpty()) { confirmMethod = methods.get(0); } assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)); SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo(); assertTrue(subjectKeyInfo.getCerts() != null); bus.shutdown(true); } @org.junit.Test public void testIssuePublicKeySAML2TokenShortKeyType() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); client.query("keyType", "PublicKey"); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); List<String> methods = assertion.getConfirmationMethods(); String confirmMethod = null; if (methods != null && !methods.isEmpty()) { confirmMethod = methods.get(0); } assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)); SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo(); assertTrue(subjectKeyInfo.getCerts() != null); bus.shutdown(true); } @org.junit.Test public void testIssueBearerSAML1Token() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml1.1"); client.query("keyType", BEARER_KEYTYPE); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); assertTrue(assertion.isSigned()); List<String> methods = assertion.getConfirmationMethods(); String confirmMethod = null; if (methods != null && !methods.isEmpty()) { confirmMethod = methods.get(0); } assertTrue(confirmMethod.contains("bearer")); bus.shutdown(true); } @org.junit.Test public void testIssueBearerSAML1TokenShorKeyType() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml1.1"); client.query("keyType", "Bearer"); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); assertTrue(assertion.isSigned()); List<String> methods = assertion.getConfirmationMethods(); String confirmMethod = null; if (methods != null && !methods.isEmpty()) { confirmMethod = methods.get(0); } assertTrue(confirmMethod.contains("bearer")); bus.shutdown(true); } @org.junit.Test public void testIssueSAML2TokenAppliesTo() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); client.query("appliesTo", DEFAULT_ADDRESS); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); bus.shutdown(true); } @org.junit.Test public void testIssueSAML2TokenUnknownAppliesTo() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); client.query("appliesTo", "https://localhost:8081/tripleit/"); Response response = client.get(); try { response.readEntity(Document.class); fail("Failure expected on an unknown AppliesTo address"); } catch (Exception ex) { // expected } bus.shutdown(true); } @org.junit.Test public void testIssueSAML2TokenClaims() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); // First check that the role isn't usually in the generated token Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token List<WSSecurityEngineResult> results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); ClaimCollection claims = SAMLUtils.getClaims(assertion); assertEquals(1, claims.size()); Claim claim = claims.get(0); String role = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"; assertNotEquals(claim.getClaimType().toString(), role); // Now get another token specifying the role client.query("claim", role); response = client.get(); assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Process the token results = processToken(assertionDoc.getDocumentElement()); assertTrue(results != null && results.size() == 1); assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); claims = SAMLUtils.getClaims(assertion); assertEquals(1, claims.size()); claim = claims.get(0); assertEquals(claim.getClaimType().toString(), role); assertEquals("ordinary-user", claim.getValues().get(0)); bus.shutdown(true); } @org.junit.Test public void testIssueSAML2TokenViaWSTrust() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); client.query("wstrustResponse", "true"); Response response = client.get(); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); validateSAMLSecurityTokenResponse(securityResponse, true); bus.shutdown(true); } @org.junit.Test public void testIssueSAML2TokenViaPOST() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Issue"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(SAML2_TOKEN_TYPE); writer.writeEndElement(); writer.writeEndElement(); Response response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); validateSAMLSecurityTokenResponse(securityResponse, true); bus.shutdown(true); } @org.junit.Test public void testExplicitlyIssueSAML2TokenViaPOST() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "issue"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Issue"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(SAML2_TOKEN_TYPE); writer.writeEndElement(); writer.writeEndElement(); Response response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); validateSAMLSecurityTokenResponse(securityResponse, true); bus.shutdown(true); } @org.junit.Test public void testExplicitlyIssueSAML1TokenViaPOST() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "issue"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Issue"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(SAML1_TOKEN_TYPE); writer.writeEndElement(); writer.writeEndElement(); Response response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); validateSAMLSecurityTokenResponse(securityResponse, false); bus.shutdown(true); } @org.junit.Test public void testValidateSAML2Token() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.path("saml2.0"); // 1. Get a token via GET Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // 2. Now validate it in the STS using POST client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "validate"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Validate"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); String tokenType = namespace + "/RSTR/Status"; writer.writeCharacters(tokenType); writer.writeEndElement(); writer.writeStartElement("wst", "ValidateTarget", namespace); StaxUtils.copy(assertionDoc.getDocumentElement(), writer); writer.writeEndElement(); writer.writeEndElement(); response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); StatusType status = null; for (Object obj : securityResponse.getAny()) { if (obj instanceof JAXBElement<?>) { JAXBElement<?> jaxbElement = (JAXBElement<?>)obj; if ("Status".equals(jaxbElement.getName().getLocalPart())) { status = (StatusType)jaxbElement.getValue(); break; } } } assertNotNull(status); // Check the token was valid String validCode = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/status/valid"; assertEquals(validCode, status.getCode()); bus.shutdown(true); } @org.junit.Test public void testRenewSAML2Token() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "issue"); // 1. Get a token via POST // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Issue"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(SAML2_TOKEN_TYPE); writer.writeEndElement(); writer.writeEndElement(); Response response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); Element token = validateSAMLSecurityTokenResponse(securityResponse, true); // 2. Now validate it in the STS using POST client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "renew"); // Create RequestSecurityToken writer = new W3CDOMStreamWriter(); writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Renew"); writer.writeEndElement(); writer.writeStartElement("wst", "RenewTarget", namespace); StaxUtils.copy(token, writer); writer.writeEndElement(); writer.writeEndElement(); response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); validateSAMLSecurityTokenResponse(securityResponse, true); bus.shutdown(true); } @org.junit.Test public void testIssueSAML2TokenPlain() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("text/plain"); client.path("saml2.0"); Response response = client.get(); String encodedAssertion = response.readEntity(String.class); assertNotNull(encodedAssertion); byte[] deflatedToken = Base64Utility.decode(encodedAssertion); InputStream inputStream = CompressionUtils.inflate(deflatedToken); Document doc = StaxUtils.read(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); // Process the token List<WSSecurityEngineResult> results = processToken(doc.getDocumentElement()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); assertTrue(assertion.isSigned()); bus.shutdown(true); } @org.junit.Test public void testIssueJWTTokenPlain() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("text/plain"); client.path("jwt"); Response response = client.get(); String token = response.readEntity(String.class); assertNotNull(token); validateJWTToken(token, null); } @org.junit.Test public void testIssueJWTTokenAppliesTo() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("text/plain"); client.path("jwt"); client.query("appliesTo", DEFAULT_ADDRESS); Response response = client.get(); String token = response.readEntity(String.class); assertNotNull(token); validateJWTToken(token, DEFAULT_ADDRESS); } @org.junit.Test public void testIssueJWTTokenClaims() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("text/plain"); client.path("jwt"); // First check that the role isn't usually in the generated token Response response = client.get(); String token = response.readEntity(String.class); assertNotNull(token); JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); JwtToken jwt = jwtConsumer.getJwtToken(); String role = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"; assertTrue(jwt.getClaim(role) == null); // Now get another token specifying the role client.query("claim", role); response = client.get(); token = response.readEntity(String.class); assertNotNull(token); // Process the token validateJWTToken(token, null); jwtConsumer = new JwsJwtCompactConsumer(token); jwt = jwtConsumer.getJwtToken(); assertEquals("ordinary-user", jwt.getClaim(role)); bus.shutdown(true); } @org.junit.Test public void testIssueJWTTokenViaPOST() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Issue"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(JWT_TOKEN_TYPE); writer.writeEndElement(); writer.writeEndElement(); Response response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); RequestedSecurityTokenType requestedSecurityToken = getRequestedSecurityToken(securityResponse); assertNotNull(requestedSecurityToken); String token = ((Element)requestedSecurityToken.getAny()).getTextContent(); assertNotNull(token); validateJWTToken(token, null); bus.shutdown(true); } @org.junit.Test public void testValidateSAMLAndIssueJWT() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("saml2.0"); // 1. Get a token via GET Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // 2. Now validate it in the STS using POST client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "validate"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Validate"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(JWT_TOKEN_TYPE); writer.writeEndElement(); writer.writeStartElement("wst", "ValidateTarget", namespace); StaxUtils.copy(assertionDoc.getDocumentElement(), writer); writer.writeEndElement(); writer.writeEndElement(); response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); StatusType status = null; for (Object obj : securityResponse.getAny()) { if (obj instanceof JAXBElement<?>) { JAXBElement<?> jaxbElement = (JAXBElement<?>)obj; if ("Status".equals(jaxbElement.getName().getLocalPart())) { status = (StatusType)jaxbElement.getValue(); break; } } } assertNotNull(status); // Check the token was valid String validCode = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/status/valid"; assertEquals(validCode, status.getCode()); // Check the token RequestedSecurityTokenType requestedSecurityToken = getRequestedSecurityToken(securityResponse); assertNotNull(requestedSecurityToken); String token = ((Element)requestedSecurityToken.getAny()).getTextContent(); assertNotNull(token); validateJWTToken(token, null); bus.shutdown(true); } @org.junit.Test public void testValidateJWTAndIssueSAML() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("text/plain"); client.path("jwt"); // 1. Get a token via GET Response response = client.get(); String token = response.readEntity(String.class); assertNotNull(token); // 2. Now validate it in the STS using POST client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); client.query("action", "validate"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Validate"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(SAML2_TOKEN_TYPE); writer.writeEndElement(); writer.writeStartElement("wst", "ValidateTarget", namespace); writer.writeStartElement(null, "TokenWrapper", null); writer.writeCharacters(token); writer.writeEndElement(); writer.writeEndElement(); writer.writeEndElement(); response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); StatusType status = null; for (Object obj : securityResponse.getAny()) { if (obj instanceof JAXBElement<?>) { JAXBElement<?> jaxbElement = (JAXBElement<?>)obj; if ("Status".equals(jaxbElement.getName().getLocalPart())) { status = (StatusType)jaxbElement.getValue(); break; } } } assertNotNull(status); // Check the token was valid String validCode = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/status/valid"; assertEquals(validCode, status.getCode()); // Check the token validateSAMLSecurityTokenResponse(securityResponse, true); bus.shutdown(true); } @org.junit.Test public void testIssueJWTTokenXMLWrapper() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/xml"); client.path("jwt"); Response response = client.get(); Document assertionDoc = response.readEntity(Document.class); assertNotNull(assertionDoc); // Discard XML wrapper validateJWTToken(assertionDoc.getDocumentElement().getFirstChild().getTextContent(), null); } @org.junit.Test public void testIssueJWTTokenJSONWrapper() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/json"); client.path("jwt"); client.get(); } @org.junit.Test public void testDefaultSAMLFormat() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("*"); client.path("saml"); Response response = client.get(); // It should be XML Document doc = response.readEntity(Document.class); assertNotNull(doc); } @org.junit.Test public void testDefaultJWTFormat() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("*"); client.path("jwt"); Response response = client.get(); // It should be XML Document doc = response.readEntity(Document.class); assertNotNull(doc); } @org.junit.Test public void testIssueSAMLTokenWithWrongAcceptType() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = STSRESTTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token"; WebClient client = WebClient.create(address, busFile.toString()); client.accept("application/json"); client.path("saml2.0"); Response response = client.get(); try { response.readEntity(Document.class); fail("Failure expected on an bad accept type"); } catch (Exception ex) { // expected } bus.shutdown(true); } private Element validateSAMLSecurityTokenResponse( RequestSecurityTokenResponseType securityResponse, boolean saml2 ) throws Exception { RequestedSecurityTokenType requestedSecurityToken = getRequestedSecurityToken(securityResponse); assertNotNull(requestedSecurityToken); // Process the token List<WSSecurityEngineResult> results = processToken((Element)requestedSecurityToken.getAny()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); if (saml2) { assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); } else { assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); } assertTrue(assertion.isSigned()); return (Element)results.get(0).get(WSSecurityEngineResult.TAG_TOKEN_ELEMENT); } private RequestedSecurityTokenType getRequestedSecurityToken(RequestSecurityTokenResponseType securityResponse) { for (Object obj : securityResponse.getAny()) { if (obj instanceof JAXBElement<?>) { JAXBElement<?> jaxbElement = (JAXBElement<?>)obj; if ("RequestedSecurityToken".equals(jaxbElement.getName().getLocalPart())) { return (RequestedSecurityTokenType)jaxbElement.getValue(); } } } return null; } private List<WSSecurityEngineResult> processToken(Element assertionElement) throws Exception { RequestData requestData = new RequestData(); requestData.setDisableBSPEnforcement(true); CallbackHandler callbackHandler = new org.apache.cxf.systest.sts.common.CommonCallbackHandler(); requestData.setCallbackHandler(callbackHandler); Crypto crypto = CryptoFactory.getInstance("serviceKeystore.properties"); requestData.setDecCrypto(crypto); requestData.setSigVerCrypto(crypto); requestData.setWsDocInfo(new WSDocInfo(assertionElement.getOwnerDocument())); Processor processor = new SAMLTokenProcessor(); return processor.handleToken(assertionElement, requestData); } private void validateJWTToken(String token, String audience) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token); JwtToken jwt = jwtConsumer.getJwtToken(); // Validate claims Assert.assertEquals("DoubleItSTSIssuer", jwt.getClaim(JwtConstants.CLAIM_ISSUER)); if (audience != null) { @SuppressWarnings("unchecked") List<String> audiences = (List<String>)jwt.getClaim(JwtConstants.CLAIM_AUDIENCE); assertEquals(1, audiences.size()); Assert.assertEquals(audience, audiences.get(0)); } Assert.assertNotNull(jwt.getClaim(JwtConstants.CLAIM_EXPIRY)); Assert.assertNotNull(jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT)); KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(Loader.getResource("keys/servicestore.jks").openStream(), "sspass".toCharArray()); Certificate cert = keystore.getCertificate("mystskey"); Assert.assertNotNull(cert); Assert.assertTrue(jwtConsumer.verifySignatureWith((X509Certificate)cert, SignatureAlgorithm.RS256)); } }