/* * JBoss, Home of Professional Open Source * * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed 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.picketlink.test.identity.federation.core.wstrust; import org.junit.Before; import org.junit.Test; import org.picketlink.common.ErrorCodes; import org.picketlink.common.constants.JBossSAMLConstants; import org.picketlink.common.constants.JBossSAMLURIConstants; import org.picketlink.common.constants.WSTrustConstants; import org.picketlink.common.exceptions.ConfigurationException; import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.fed.WSTrustException; import org.picketlink.common.util.Base64; import org.picketlink.common.util.DocumentUtil; import org.picketlink.config.federation.STSType; import org.picketlink.config.federation.parsers.STSConfigParser; import org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider; import org.picketlink.identity.federation.core.parsers.wst.WSTrustParser; import org.picketlink.identity.federation.core.saml.v1.SAML11Constants; import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator; import org.picketlink.identity.federation.core.util.SOAPUtil; import org.picketlink.identity.federation.core.wstrust.PicketLinkSTS; import org.picketlink.identity.federation.core.wstrust.PicketLinkSTSConfiguration; import org.picketlink.identity.federation.core.wstrust.STSConfiguration; import org.picketlink.identity.federation.core.wstrust.StandardRequestHandler; import org.picketlink.identity.federation.core.wstrust.WSTrustRequestHandler; import org.picketlink.identity.federation.core.wstrust.WSTrustUtil; import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAML11TokenProvider; import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAML20TokenProvider; import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil; import org.picketlink.identity.federation.core.wstrust.wrappers.BaseRequestSecurityTokenResponse; import org.picketlink.identity.federation.core.wstrust.wrappers.Lifetime; import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken; import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenResponse; import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenResponseCollection; import org.picketlink.identity.federation.core.wstrust.writers.WSTrustRequestWriter; import org.picketlink.identity.federation.saml.v1.assertion.SAML11AssertionType; import org.picketlink.identity.federation.saml.v1.assertion.SAML11AuthenticationStatementType; import org.picketlink.identity.federation.saml.v1.assertion.SAML11NameIdentifierType; import org.picketlink.identity.federation.saml.v1.assertion.SAML11StatementAbstractType; import org.picketlink.identity.federation.saml.v1.assertion.SAML11SubjectConfirmationType; import org.picketlink.identity.federation.saml.v1.assertion.SAML11SubjectType; import org.picketlink.identity.federation.saml.v2.assertion.AssertionType; import org.picketlink.identity.federation.saml.v2.assertion.AudienceRestrictionType; import org.picketlink.identity.federation.saml.v2.assertion.ConditionAbstractType; import org.picketlink.identity.federation.saml.v2.assertion.ConditionsType; import org.picketlink.identity.federation.saml.v2.assertion.NameIDType; import org.picketlink.identity.federation.saml.v2.assertion.SubjectConfirmationDataType; import org.picketlink.identity.federation.saml.v2.assertion.SubjectConfirmationType; import org.picketlink.identity.federation.saml.v2.assertion.SubjectType; import org.picketlink.identity.federation.ws.trust.BinarySecretType; import org.picketlink.identity.federation.ws.trust.CancelTargetType; import org.picketlink.identity.federation.ws.trust.ComputedKeyType; import org.picketlink.identity.federation.ws.trust.EntropyType; import org.picketlink.identity.federation.ws.trust.OnBehalfOfType; import org.picketlink.identity.federation.ws.trust.RenewTargetType; import org.picketlink.identity.federation.ws.trust.RequestedProofTokenType; import org.picketlink.identity.federation.ws.trust.RequestedReferenceType; import org.picketlink.identity.federation.ws.trust.RequestedSecurityTokenType; import org.picketlink.identity.federation.ws.trust.StatusType; import org.picketlink.identity.federation.ws.trust.UseKeyType; import org.picketlink.identity.federation.ws.trust.ValidateTargetType; import org.picketlink.identity.federation.ws.wss.secext.KeyIdentifierType; import org.picketlink.identity.federation.ws.wss.secext.SecurityTokenReferenceType; import org.picketlink.identity.xmlsec.w3.xmldsig.KeyInfoType; import org.picketlink.identity.xmlsec.w3.xmldsig.KeyValueType; import org.picketlink.identity.xmlsec.w3.xmldsig.RSAKeyValueType; import org.picketlink.identity.xmlsec.w3.xmldsig.X509CertificateType; import org.picketlink.identity.xmlsec.w3.xmldsig.X509DataType; import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.xml.datatype.DatatypeConstants; import javax.xml.namespace.QName; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.ws.EndpointReference; import javax.xml.ws.WebServiceContext; import javax.xml.ws.WebServiceException; import javax.xml.ws.handler.MessageContext; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.security.KeyFactory; import java.security.KeyStore; import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * <p> * This {@code TestCase} tests the behavior of the {@code PicketLinkSTS} service. * </p> * * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a> */ public class PicketLinkSTSUnitTestCase { private TestSTS tokenService; /* * (non-Javadoc) * * @see junit.framework.TestCase#setUp() */ @Before public void setUp() throws Exception { // for testing purposes we can instantiate the TestSTS as a regular POJO. this.tokenService = new TestSTS(); TestContext context = new TestContext(); context.setUserPrincipal(new TestPrincipal("jduke")); this.tokenService.setContext(context); } /** * <p> * This test verifies that the STS service can read and load all configuration parameters correctly. The * configuration file * (picketlink-sts.xml) looks like the following: * * <pre> * <PicketLinkSTS xmlns="urn:picketlink:identity-federation:config:1.0" * STSName="Test STS" TokenTimeout="7200" EncryptToken="true"> * <KeyProvider ClassName="org.jboss.identity.federation.bindings.tomcat.KeyStoreKeyManager"> * <Auth Key="KeyStoreURL" Value="keystore/sts_keystore.jks"/> * <Auth Key="KeyStorePass" Value="testpass"/> * <Auth Key="SigningKeyAlias" Value="sts"/> * <Auth Key="SigningKeyPass" Value="keypass"/> * <ValidatingAlias Key="http://services.testcorp.org/provider1" * Value="service1"/> * <ValidatingAlias Key="http://services.testcorp.org/provider2" * Value="service2"/> * </KeyProvider> * <RequestHandler>org.jboss.identity.federation.core.wstrust.StandardRequestHandler</RequestHandler> * <TokenProviders> * <TokenProvider ProviderClass="org.jboss.test.identity.federation.bindings.trust.SpecialTokenProvider" * TokenType="http://www.tokens.org/SpecialToken" * TokenElement="SpecialToken" * TokenElementNS="http://www.tokens.org"> * <Property Key="Property1" Value="Value1"/> * <Property Key="Property2" Value="Value2"/> * </TokenProvider> * <TokenProvider ProviderClass="org.jboss.identity.federation.core.wstrust.SAML11TokenProvider" * TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" * TokenElement="Assertion" * TokenElementNS="urn:oasis:names:tc:SAML:1.0:assertion"/> * <TokenProvider ProviderClass="org.jboss.identity.federation.core.wstrust.SAML20TokenProvider" * TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" * TokenElement="Assertion" * TokenElementNS="urn:oasis:names:tc:SAML:2.0:assertion"/>/> * </TokenProviders> * <ServiceProviders> * <ServiceProvider Endpoint="http://services.testcorp.org/provider1" * TokenType="http://www.tokens.org/SpecialToken" * TruststoreAlias="service1"/> * <ServiceProvider Endpoint="http://services.testcorp.org/provider2" * TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" * TruststoreAlias="service2"/> * </ServiceProviders> * </PicketLinkSTS> * * </pre> * * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testSTSConfiguration() throws Exception { // make the STS read the configuration file. STSConfiguration config = this.tokenService.getConfiguration(); // check the values that have been configured. assertEquals("Unexpected service name", "Test STS", config.getSTSName()); assertEquals("Unexpected token timeout value", 7200 * 1000, config.getIssuedTokenTimeout()); assertFalse("Encrypt token should be true", config.encryptIssuedToken()); WSTrustRequestHandler handler = config.getRequestHandler(); assertNotNull("Unexpected null request handler found", handler); assertTrue("Unexpected request handler type", handler instanceof StandardRequestHandler); // check the token type -> token provider mapping. SecurityTokenProvider provider = config.getProviderForTokenType("http://www.tokens.org/SpecialToken"); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SpecialTokenProvider); Map<String, String> properties = ((SpecialTokenProvider) provider).getProperties(); assertNotNull("Unexpected null properties map", properties); assertEquals("Invalid property found", "Value1", properties.get("Property1")); assertEquals("Invalid property found", "Value2", properties.get("Property2")); provider = config.getProviderForTokenType(SAMLUtil.SAML2_TOKEN_TYPE); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SAML20TokenProvider); provider = config.getProviderForTokenType(SAMLUtil.SAML11_TOKEN_TYPE); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SAML11TokenProvider); assertNull(config.getProviderForTokenType("unexistentType")); // check the service provider -> token provider mapping. provider = config.getProviderForService("http://services.testcorp.org/provider1"); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SpecialTokenProvider); provider = config.getProviderForService("http://services.testcorp.org/provider2"); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SAML20TokenProvider); assertNull(config.getProviderForService("http://invalid.service/service")); String family = SecurityTokenProvider.FAMILY_TYPE.WS_TRUST.toString(); // check the token element and namespace -> token provider mapping. provider = config.getProviderForTokenElementNS(family, new QName("http://www.tokens.org", "SpecialToken")); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SpecialTokenProvider); provider = config.getProviderForTokenElementNS(family, new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ASSERTION.get())); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SAML20TokenProvider); provider = config.getProviderForTokenElementNS(family, new QName(SAML11Constants.ASSERTION_11_NSURI, JBossSAMLConstants.ASSERTION.get())); assertNotNull("Unexpected null token provider", provider); assertTrue("Unexpected token provider type", provider instanceof SAML11TokenProvider); assertNull(config.getProviderForTokenElementNS(family, new QName("InvalidNamespace", "SpecialToken"))); // check the service provider -> token type mapping. assertEquals("Invalid token type for service provider 1", "http://www.tokens.org/SpecialToken", config.getTokenTypeForService("http://services.testcorp.org/provider1")); assertEquals("Invalid token type for service provider 2", SAMLUtil.SAML2_TOKEN_TYPE, config.getTokenTypeForService("http://services.testcorp.org/provider2")); assertNull(config.getTokenTypeForService("http://invalid.service/service")); // check the keystore configuration. assertNotNull("Invalid null STS key pair", config.getSTSKeyPair()); assertNotNull("Invalid null STS public key", config.getSTSKeyPair().getPublic()); assertNotNull("Invalid null STS private key", config.getSTSKeyPair().getPrivate()); assertNotNull("Invalid null validating key for service provider 1", config.getServiceProviderPublicKey("http://services.testcorp.org/provider1")); assertNotNull("Invalid null validating key for service provider 2", config.getServiceProviderPublicKey("http://services.testcorp.org/provider2")); } /** * <p> * This tests sends a security token request to PicketLinkSTS custom {@code SpecialTokenProvider}. The returned * response is * verified to make sure the expected tokens have been returned by the service. The token that is generated in this * test * looks as follows: * * <pre> * <token:SpecialToken xmlns:token="http://www.tokens.org" TokenType="http://www.tokens.org/SpecialToken"> * Principal:sguilhen * </token:SpecialToken> * </pre> * * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeCustom() throws Exception { // create a simple token request, asking for a "special" test token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, "http://www.tokens.org/SpecialToken", null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. this.validateCustomTokenResponse(baseResponse); } /** * <p> * This tests sends a SAMLV2.0 security token request to PicketLinkSTS. This request should be handled by the * {@code SAML11TokenProvider} and should result in a SAMLV1.1 assertion. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML11() throws Exception { // create a simple token request, asking for a SAMLv1.1 token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML11_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); InputStream is = DocumentUtil.getSourceAsStream(responseMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser().parse(is); // validate the security token response. this.validateSAML11AssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML11_BEARER_URI); } /** * <p> * This tests sends a SAMLV2.0 security token request to PicketLinkSTS. This request should be handled by the * standard * {@code SAML20TokenProvider} and should result in a SAMLV2.0 assertion that looks like the following: * * <pre> * <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" * xmlns:ds="http://www.w3.org/2000/09/xmldsig#" * xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" * ID="ID-cc541137-74dc-4fc0-8bcc-7e9e3a4c899d" * IssueInstant="2009-05-29T18:02:13.458Z"> * <saml2:Issuer> * PicketLinkSTS * </saml2:Issuer> * <saml2:Subject> * <saml2:NameID NameQualifier="http://www.jboss.org"> * sguilhen * </saml2:NameID> * <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"/> * </saml2:Subject> * <saml2:Conditions NotBefore="2009-05-29T18:02:13.458Z" NotOnOrAfter="2009-05-29T19:02:13.458Z"> * <saml2:AudienceRestriction> * <saml2:Audience> * http://services.testcorp.org/provider2 * </saml2:Audience> * </saml2:AudienceRestriction> * </saml2:Conditions> * <ds:Signature> * ... * </ds:Signature> * </saml2:Assertion> * </pre> * * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20() throws Exception { // create a simple token request, asking for a SAMLv2.0 token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); InputStream is = DocumentUtil.getSourceAsStream(responseMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser().parse(is); // validate the security token response. this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); } /** * Use SOAP12 * * @throws Exception * @see #testInvokeSAML20() */ @Test public void testInvokeSAML20WithSOAP12() throws Exception { // create a simple token request, asking for a SAMLv2.0 token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); this.tokenService.setSoap12(true); // Set to SOAP12 try { // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); InputStream is = DocumentUtil.getSourceAsStream(responseMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser().parse(is); // validate the security token response. this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); } finally { this.tokenService.setSoap12(false); } } /** * <p> * This test requests a token to the STS using the {@code AppliesTo} to identify the service provider. The STS must * be able * to find out the type of the token that must be issued using the service provider URI. In this specific case, the * request * should be handled by the custom {@code SpecialTokenProvider}. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeCustomAppliesTo() throws Exception { // create a simple token request, this time using the applies to get to the token type. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider1"); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. this.validateCustomTokenResponse(baseResponse); } /** * <p> * This test requests a token to the STS using the {@code AppliesTo} to identify the service provider with regex. * The STS must be able * to find out the type of the token that must be issued using the service provider URI. In this specific case, the * request * should be handled by the custom {@code SpecialTokenProvider}. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeCustomAppliesToRegEx() throws Exception { // create a simple token request, this time using the applies to get to the token type. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorpone.org/provider1/a/b"); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. this.validateCustomTokenResponse(baseResponse); } /** * <p> * This test requests a token to the STS using the {@code AppliesTo} to identify the service provider. The STS must * be able * to find out the type of the token that must be issued using the service provider URI. In this specific case, the * request * should be handled by the standard {@code SAML20TokenProvider}. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20AppliesTo() throws Exception { RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider2"); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); // in this scenario, the conditions section should have an audience restriction. ConditionsType conditions = assertion.getConditions(); assertEquals("Unexpected restriction list size", 1, conditions.getConditions().size()); ConditionAbstractType abstractType = conditions.getConditions().get(0); assertTrue("Unexpected restriction type", abstractType instanceof AudienceRestrictionType); AudienceRestrictionType audienceRestriction = (AudienceRestrictionType) abstractType; assertEquals("Unexpected audience restriction list size", 1, audienceRestriction.getAudience().size()); assertEquals("Unexpected audience restriction item", "http://services.testcorp.org/provider2", audienceRestriction .getAudience().get(0).toString()); } /** * <p> * This test requests a token to the STS using the {@code AppliesTo} to identify the service provider with regex. * The STS must be able * to find out the type of the token that must be issued using the service provider URI. In this specific case, the * request * should be handled by the standard {@code SAML20TokenProvider}. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20AppliesToRegEx() throws Exception { String serviceName = "http://services.testcorptwo.org/provider2/a/b"; RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, serviceName); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); // in this scenario, the conditions section should have an audience restriction. ConditionsType conditions = assertion.getConditions(); assertEquals("Unexpected restriction list size", 1, conditions.getConditions().size()); ConditionAbstractType abstractType = conditions.getConditions().get(0); assertTrue("Unexpected restriction type", abstractType instanceof AudienceRestrictionType); AudienceRestrictionType audienceRestriction = (AudienceRestrictionType) abstractType; assertEquals("Unexpected audience restriction list size", 1, audienceRestriction.getAudience().size()); assertEquals("Unexpected audience restriction item", serviceName, audienceRestriction .getAudience().get(0).toString()); } /** * <p> * This test requests a SAMLV2.0 assertion on behalf of another identity. The STS must issue an assertion for the * identity * contained in the {@code OnBehalfOf} section of the WS-Trust request (and not for the identity that sent the * request). * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20OnBehalfOf() throws Exception { // create a simple token request, asking for a SAMLv2.0 token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); OnBehalfOfType onBehalfOf = WSTrustUtil.createOnBehalfOfWithUsername("anotherduke", "id"); request.setOnBehalfOf(onBehalfOf); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response (assertion principal should be anotherduke as specified by OnBehalfOf). this.validateSAMLAssertionResponse(baseResponse, "testcontext", "anotherduke", SAMLUtil.SAML2_SENDER_VOUCHES_URI); } /** * <p> * This test requests a SAMLV2.0 assertion and requires a symmetric key to be used as a proof-of-possession token. * As the * request doesn't contain any client-specified key, the STS is responsible for generating a random key and use this * key as * the proof token. The WS-Trust response should contain the STS-generated key. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithSTSGeneratedSymmetricKey() throws Exception { // create a simple token request, asking for a SAMLv2.0 token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider2"); // add a symmetric key type to the request, but don't supply any client key - STS should generate one. request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_SYMMETRIC)); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_SYMMETRIC, null, false); // check if the response contains the STS-generated key. RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); RequestedProofTokenType proofToken = response.getRequestedProofToken(); assertNotNull("Unexpected null proof token", proofToken); assertTrue(proofToken.getAny().get(0) instanceof BinarySecretType); BinarySecretType serverBinarySecret = (BinarySecretType) proofToken.getAny().get(0); assertNotNull("Unexpected null secret", serverBinarySecret.getValue()); // default key size is 128 bits (16 bytes). byte[] encodedSecret = serverBinarySecret.getValue(); assertEquals("Unexpected secret size", 16, Base64.decode(encodedSecret, 0, encodedSecret.length).length); } /** * <p> * This test requests a SAMLV2.0 assertion and requires a symmetric key to be used as a proof-of-possession token with a ServiceProvider from a ServiceProvidersRegEx list. * As the * request doesn't contain any client-specified key, the STS is responsible for generating a random key and use this * key as * the proof token. The WS-Trust response should contain the STS-generated key. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithSTSGeneratedSymmetricKeyRegEx() throws Exception { // create a simple token request, asking for a SAMLv2.0 token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorptwo.org/provider2/a/b"); // add a symmetric key type to the request, but don't supply any client key - STS should generate one. request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_SYMMETRIC)); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_SYMMETRIC, null, false); // check if the response contains the STS-generated key. RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); RequestedProofTokenType proofToken = response.getRequestedProofToken(); assertNotNull("Unexpected null proof token", proofToken); assertTrue(proofToken.getAny().get(0) instanceof BinarySecretType); BinarySecretType serverBinarySecret = (BinarySecretType) proofToken.getAny().get(0); assertNotNull("Unexpected null secret", serverBinarySecret.getValue()); // default key size is 128 bits (16 bytes). byte[] encodedSecret = serverBinarySecret.getValue(); assertEquals("Unexpected secret size", 16, Base64.decode(encodedSecret, 0, encodedSecret.length).length); } /** * <p> * This test requests a SAMLV2.0 assertion and requires a symmetric key to be used as a proof-of-possession token. * In this * case, the client supplies a secret key in the WS-Trust request, so the STS should combine the client- specified * key with * the STS-generated key and use this combined key as the proof token. The WS-Trust response should include the STS * key to * allow reconstruction of the combined key and the algorithm used to combine the keys. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithCombinedSymmetricKey() throws Exception { // create a 64-bit random client secret. byte[] clientSecret = WSTrustUtil.createRandomSecret(8); BinarySecretType clientBinarySecret = new BinarySecretType(); clientBinarySecret.setType(WSTrustConstants.BS_TYPE_NONCE); clientBinarySecret.setValue(Base64.encodeBytes(clientSecret).getBytes()); // set the client secret in the client entropy. EntropyType clientEntropy = new EntropyType(); clientEntropy.addAny(clientBinarySecret); // create a token request specifying the key type, key size, and client entropy. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider2"); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_SYMMETRIC)); request.setEntropy(clientEntropy); request.setKeySize(64); // invoke the token service. Source requestMessage = this.createSourceFromRequest(request); Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_SYMMETRIC, null, false); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); RequestedProofTokenType proofToken = response.getRequestedProofToken(); assertNotNull("Unexpected null proof token", proofToken); assertTrue(proofToken.getAny().get(0) instanceof ComputedKeyType); ComputedKeyType computedKey = (ComputedKeyType) proofToken.getAny().get(0); assertEquals("Unexpected computed key algorithm", WSTrustConstants.CK_PSHA1, computedKey.getAlgorithm()); // server entropy must have been included in the response to allow reconstruction of the computed key. EntropyType serverEntropy = response.getEntropy(); assertNotNull("Unexpected null server entropy"); assertEquals("Invalid number of elements in server entropy", 1, serverEntropy.getAny().size()); BinarySecretType serverBinarySecret = (BinarySecretType) serverEntropy.getAny().get(0); assertEquals("Unexpected binary secret type", WSTrustConstants.BS_TYPE_NONCE, serverBinarySecret.getType()); assertNotNull("Unexpected null secret value", serverBinarySecret.getValue()); // get the base64 decoded byte[] encodedSecret = serverBinarySecret.getValue(); assertEquals("Unexpected secret size", 8, Base64.decode(encodedSecret, 0, encodedSecret.length).length); } /** * <p> * This test requests a SAMLV2.0 assertion and requires a symmetric key to be used as a proof-of-possession token with a ServiceProvider from a ServiceProvidersRegEx list. * In this * case, the client supplies a secret key in the WS-Trust request, so the STS should combine the client- specified * key with * the STS-generated key and use this combined key as the proof token. The WS-Trust response should include the STS * key to * allow reconstruction of the combined key and the algorithm used to combine the keys. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithCombinedSymmetricKeyRegEx() throws Exception { // create a 64-bit random client secret. byte[] clientSecret = WSTrustUtil.createRandomSecret(8); BinarySecretType clientBinarySecret = new BinarySecretType(); clientBinarySecret.setType(WSTrustConstants.BS_TYPE_NONCE); clientBinarySecret.setValue(Base64.encodeBytes(clientSecret).getBytes()); // set the client secret in the client entropy. EntropyType clientEntropy = new EntropyType(); clientEntropy.addAny(clientBinarySecret); // create a token request specifying the key type, key size, and client entropy. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorptwo.org/provider2/a/b"); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_SYMMETRIC)); request.setEntropy(clientEntropy); request.setKeySize(64); // invoke the token service. Source requestMessage = this.createSourceFromRequest(request); Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_SYMMETRIC, null, false); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); RequestedProofTokenType proofToken = response.getRequestedProofToken(); assertNotNull("Unexpected null proof token", proofToken); assertTrue(proofToken.getAny().get(0) instanceof ComputedKeyType); ComputedKeyType computedKey = (ComputedKeyType) proofToken.getAny().get(0); assertEquals("Unexpected computed key algorithm", WSTrustConstants.CK_PSHA1, computedKey.getAlgorithm()); // server entropy must have been included in the response to allow reconstruction of the computed key. EntropyType serverEntropy = response.getEntropy(); assertNotNull("Unexpected null server entropy"); assertEquals("Invalid number of elements in server entropy", 1, serverEntropy.getAny().size()); BinarySecretType serverBinarySecret = (BinarySecretType) serverEntropy.getAny().get(0); assertEquals("Unexpected binary secret type", WSTrustConstants.BS_TYPE_NONCE, serverBinarySecret.getType()); assertNotNull("Unexpected null secret value", serverBinarySecret.getValue()); // get the base64 decoded byte[] encodedSecret = serverBinarySecret.getValue(); assertEquals("Unexpected secret size", 8, Base64.decode(encodedSecret, 0, encodedSecret.length).length); } /** * <p> * This test requests a SAMLV2.0 assertion and sends a X.509 certificate to be used as the proof-of-possession * token. The * STS must include the specified certificate in the SAML subject confirmation. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithCertificate() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider2"); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_PUBLIC)); // include a UseKey section that specifies the certificate in the request. Certificate certificate = this.getCertificate("keystore/sts_keystore.jks", "testpass", "service1"); UseKeyType useKey = new UseKeyType(); useKey.add(Base64.encodeBytes(certificate.getEncoded()).getBytes()); request.setUseKey(useKey); // invoke the token service. Source requestMessage = this.createSourceFromRequest(request); Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_PUBLIC, certificate, false); } /** * <p> * This test requests a SAMLV2.0 assertion and sends a X.509 certificate to be used as the proof-of-possession with a ServiceProvider from a ServiceProvidersRegEx list. * token. The * STS must include the specified certificate in the SAML subject confirmation. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithCertificateRegEx() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorptwo.org/provider2/a/b/c/d"); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_PUBLIC)); // include a UseKey section that specifies the certificate in the request. Certificate certificate = this.getCertificate("keystore/sts_keystore.jks", "testpass", "service1"); UseKeyType useKey = new UseKeyType(); useKey.add(Base64.encodeBytes(certificate.getEncoded()).getBytes()); request.setUseKey(useKey); // invoke the token service. Source requestMessage = this.createSourceFromRequest(request); Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_PUBLIC, certificate, false); } /** * <p> * This test requests a SAMLV2.0 assertion and sends a public key to be used as the proof-of-possession token. The * STS must * include the specified public key in the SAML subject confirmation. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithPublicKey() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider2"); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_PUBLIC)); // include a UseKey section that sets the public key in the request. Certificate certificate = this.getCertificate("keystore/sts_keystore.jks", "testpass", "service1"); KeyValueType keyValue = WSTrustUtil.createKeyValue(certificate.getPublicKey()); UseKeyType useKey = new UseKeyType(); useKey.add(keyValue); request.setUseKey(useKey); // invoke the token service. Source requestMessage = this.createSourceFromRequest(request); Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_PUBLIC, certificate, true); } /** * <p> * This test requests a SAMLV2.0 assertion and sends a public key to be used as the proof-of-possession token with a ServiceProvider from a ServiceProvidersRegEx list. * The STS must * include the specified public key in the SAML subject confirmation. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20WithPublicKeyRegEx() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorptwo.org/provider2/abb/bcc/cdd/deee"); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_PUBLIC)); // include a UseKey section that sets the public key in the request. Certificate certificate = this.getCertificate("keystore/sts_keystore.jks", "testpass", "service1"); KeyValueType keyValue = WSTrustUtil.createKeyValue(certificate.getPublicKey()); UseKeyType useKey = new UseKeyType(); useKey.add(keyValue); request.setUseKey(useKey); // invoke the token service. Source requestMessage = this.createSourceFromRequest(request); Source responseMessage = this.tokenService.invoke(requestMessage); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the security token response. AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_HOLDER_OF_KEY_URI); // validate the holder of key contents. SubjectConfirmationType subjConfirmation = assertion.getSubject().getConfirmation().get(0); this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_PUBLIC, certificate, true); } /** * <p> * This test case first generates a SAMLV1.1 assertion and then sends a WS-Trust validate message to the STS to get * the * assertion validated, checking the validation results. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML11Validate() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML11_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAML11AssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML11_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertion = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken().getAny() .get(0); // now construct a WS-Trust validate request with the generated assertion. request = this.createRequest("validatecontext", WSTrustConstants.VALIDATE_REQUEST, WSTrustConstants.STATUS_TYPE, null); ValidateTargetType validateTarget = new ValidateTargetType(); validateTarget.add(assertion); request.setValidateTarget(validateTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the response contents. assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "validatecontext", response.getContext()); assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString()); StatusType status = response.getStatus(); assertNotNull("Unexpected null status", status); assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_VALID, status.getCode()); assertEquals("Unexpected status reason", "SAMLV1.1 Assertion successfuly validated", status.getReason()); // now let's temper the SAML assertion and try to validate it again. // assertion.getAttributeNode("Issuer").setNodeValue("ABC"); // request.getValidateTarget().add(assertion); // Source theRequest = this.createSourceFromRequest(request); // responseMessage = this.tokenService.invoke(theRequest); // collection = (RequestSecurityTokenResponseCollection) parser.parse(DocumentUtil // .getSourceAsStream(responseMessage)); // assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); // response = collection.getRequestSecurityTokenResponses().get(0); // assertEquals("Unexpected response context", "validatecontext", response.getContext()); // assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString()); // status = response.getStatus(); // assertNotNull("Unexpected null status", status); // assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode()); // assertEquals("Unexpected status reason", "Validation failure: digital signature is invalid", // status.getReason()); } /** * <p> * This test case first generates a SAMLV2.0 assertion and then sends a WS-Trust validate message to the STS to get * the * assertion validated, checking the validation results. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20Validate() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertion = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken().getAny() .get(0); // now construct a WS-Trust validate request with the generated assertion. request = this.createRequest("validatecontext", WSTrustConstants.VALIDATE_REQUEST, WSTrustConstants.STATUS_TYPE, null); ValidateTargetType validateTarget = new ValidateTargetType(); validateTarget.add(assertion); request.setValidateTarget(validateTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the response contents. assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "validatecontext", response.getContext()); assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString()); StatusType status = response.getStatus(); assertNotNull("Unexpected null status", status); assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_VALID, status.getCode()); assertEquals("Unexpected status reason", "SAMLV2.0 Assertion successfuly validated", status.getReason()); // now let's temper the SAML assertion and try to validate it again. assertion.setAttribute("Version", "X"); request.getValidateTarget().add(assertion); Source theRequest = this.createSourceFromRequest(request); responseMessage = this.tokenService.invoke(theRequest); collection = (RequestSecurityTokenResponseCollection) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "validatecontext", response.getContext()); assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString()); status = response.getStatus(); assertNotNull("Unexpected null status", status); assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode()); assertEquals("Unexpected status reason", "Validation failure: digital signature is invalid", status.getReason()); } /** * <p> * This test case first generates a SAMLV1.1 assertion and then sends a WS-Trust renew message to the STS to get * the * assertion renewed (i.e. get a new assertion with an updated lifetime). * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML11Renew() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML11_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAML11AssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML11_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken() .getAny().get(0); // now construct a WS-Trust renew request with the generated assertion. request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, SAMLUtil.SAML11_TOKEN_TYPE, null); RenewTargetType renewTarget = new RenewTargetType(); renewTarget.add(assertionElement); request.setRenewTarget(renewTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the renew response contents and get the renewed token. this.validateSAML11AssertionResponse(baseResponse, "renewcontext", "jduke", SAMLUtil.SAML11_BEARER_URI); collection = (RequestSecurityTokenResponseCollection) baseResponse; Element renewedAssertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0) .getRequestedSecurityToken().getAny().get(0); // compare the assertions, checking if the lifetime has been updated. SAML11AssertionType originalAssertion = SAMLUtil.saml11FromElement(assertionElement); SAML11AssertionType renewedAssertion = SAMLUtil.saml11FromElement(renewedAssertionElement); // assertions should have different ids and lifetimes. assertFalse("Renewed assertion should have a unique id", originalAssertion.getID().equals(renewedAssertion.getID())); assertEquals(DatatypeConstants.LESSER, originalAssertion.getConditions().getNotBefore().compare(renewedAssertion.getConditions().getNotBefore())); assertEquals(DatatypeConstants.LESSER, originalAssertion.getConditions().getNotOnOrAfter().compare(renewedAssertion.getConditions().getNotOnOrAfter())); } /** * <p> * This test case first generates a SAMLV2.0 assertion and then sends a WS-Trust renew message to the STS to get * the * assertion renewed (i.e. get a new assertion with an updated lifetime). * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20Renew() throws Exception { // create a simple token request, using applies-to to identify the token type. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorp.org/provider2"); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken() .getAny().get(0); // now construct a WS-Trust renew request with the generated assertion. request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); RenewTargetType renewTarget = new RenewTargetType(); renewTarget.add(assertionElement); request.setRenewTarget(renewTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the renew response contents and get the renewed token. this.validateSAMLAssertionResponse(baseResponse, "renewcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); collection = (RequestSecurityTokenResponseCollection) baseResponse; Element renewedAssertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0) .getRequestedSecurityToken().getAny().get(0); // compare the assertions, checking if the lifetime has been updated. AssertionType originalAssertion = SAMLUtil.fromElement(assertionElement); AssertionType renewedAssertion = SAMLUtil.fromElement(renewedAssertionElement); // assertions should have different ids and lifetimes. assertFalse("Renewed assertion should have a unique id", originalAssertion.getID().equals(renewedAssertion.getID())); assertEquals(DatatypeConstants.LESSER, originalAssertion.getConditions().getNotBefore().compare(renewedAssertion.getConditions().getNotBefore())); assertEquals(DatatypeConstants.LESSER, originalAssertion.getConditions().getNotOnOrAfter().compare(renewedAssertion.getConditions().getNotOnOrAfter())); } /** * <p> * This test case first generates a SAMLV2.0 assertion and then sends a WS-Trust renew message to the STS to get * the * assertion renewed (i.e. get a new assertion with an updated lifetime) with a ServiceProvider from a ServiceProvidersRegEx list. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20RenewRegEx() throws Exception { // create a simple token request, using applies-to to identify the token type. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, "http://services.testcorptwo.org/provider2/abbbbbb/bccccccc/cddddddd/deeeeeee"); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken() .getAny().get(0); // now construct a WS-Trust renew request with the generated assertion. request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); RenewTargetType renewTarget = new RenewTargetType(); renewTarget.add(assertionElement); request.setRenewTarget(renewTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the renew response contents and get the renewed token. this.validateSAMLAssertionResponse(baseResponse, "renewcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); collection = (RequestSecurityTokenResponseCollection) baseResponse; Element renewedAssertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0) .getRequestedSecurityToken().getAny().get(0); // compare the assertions, checking if the lifetime has been updated. AssertionType originalAssertion = SAMLUtil.fromElement(assertionElement); AssertionType renewedAssertion = SAMLUtil.fromElement(renewedAssertionElement); // assertions should have different ids and lifetimes. assertFalse("Renewed assertion should have a unique id", originalAssertion.getID().equals(renewedAssertion.getID())); assertEquals(DatatypeConstants.LESSER, originalAssertion.getConditions().getNotBefore().compare(renewedAssertion.getConditions().getNotBefore())); assertEquals(DatatypeConstants.LESSER, originalAssertion.getConditions().getNotOnOrAfter().compare(renewedAssertion.getConditions().getNotOnOrAfter())); } /** * <p> * This test case first generates a SAMLV1.1 assertion and then sends a WS-Trust cancel message to the STS to cancel * the * assertion. A canceled assertion cannot be renewed or considered valid anymore. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML11Cancel() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML11_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAML11AssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML11_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertion = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken().getAny() .get(0); // now construct a WS-Trust cancel request with the generated assertion. request = this.createRequest("cancelcontext", WSTrustConstants.CANCEL_REQUEST, null, null); CancelTargetType cancelTarget = new CancelTargetType(); cancelTarget.add(assertion); request.setCancelTarget(cancelTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the response contents. assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "cancelcontext", response.getContext()); assertNotNull("Cancel response should contain a RequestedTokenCancelled element", response.getRequestedTokenCancelled()); // try to validate the canceled assertion. request = this.createRequest("validatecontext", WSTrustConstants.VALIDATE_REQUEST, null, null); ValidateTargetType validateTarget = new ValidateTargetType(); validateTarget.add(assertion); request.setValidateTarget(validateTarget); // the response should contain a status indicating that the token is not valid. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); collection = (RequestSecurityTokenResponseCollection) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "validatecontext", response.getContext()); assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString()); StatusType status = response.getStatus(); assertNotNull("Unexpected null status", status); assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode()); assertEquals("Unexpected status reason", "Validation failure: assertion with id " + assertion.getAttribute("AssertionID") + " has been canceled", status.getReason()); // now try to renew the canceled assertion. request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, null, null); RenewTargetType renewTarget = new RenewTargetType(); renewTarget.add(assertion); request.setRenewTarget(renewTarget); // we should receive an exception when renewing the token. try { this.tokenService.invoke(this.createSourceFromRequest(request)); fail("Renewing a canceled token should result in an exception being thrown"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains("has been canceled and cannot be renewed") == false) throw new RuntimeException("Unexpected exception message"); } } /** * <p> * This test case first generates a SAMLV2.0 assertion and then sends a WS-Trust cancel message to the STS to cancel * the * assertion. A canceled assertion cannot be renewed or considered valid anymore. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeSAML20Cancel() throws Exception { // create a simple token request. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. Source responseMessage = this.tokenService.invoke(requestMessage); WSTrustParser parser = new WSTrustParser(); BaseRequestSecurityTokenResponse baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil .getSourceAsStream(responseMessage)); // validate the response and get the SAML assertion from the request. this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; Element assertion = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken().getAny() .get(0); // now construct a WS-Trust cancel request with the generated assertion. request = this.createRequest("cancelcontext", WSTrustConstants.CANCEL_REQUEST, null, null); CancelTargetType cancelTarget = new CancelTargetType(); cancelTarget.add(assertion); request.setCancelTarget(cancelTarget); // invoke the token service. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); baseResponse = (BaseRequestSecurityTokenResponse) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); // validate the response contents. assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "cancelcontext", response.getContext()); assertNotNull("Cancel response should contain a RequestedTokenCancelled element", response.getRequestedTokenCancelled()); // try to validate the canceled assertion. request = this.createRequest("validatecontext", WSTrustConstants.VALIDATE_REQUEST, null, null); ValidateTargetType validateTarget = new ValidateTargetType(); validateTarget.add(assertion); request.setValidateTarget(validateTarget); // the response should contain a status indicating that the token is not valid. responseMessage = this.tokenService.invoke(this.createSourceFromRequest(request)); collection = (RequestSecurityTokenResponseCollection) parser.parse(DocumentUtil.getSourceAsStream(responseMessage)); assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "validatecontext", response.getContext()); assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString()); StatusType status = response.getStatus(); assertNotNull("Unexpected null status", status); assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode()); assertEquals("Unexpected status reason", "Validation failure: assertion with id " + assertion.getAttribute("ID") + " has been canceled", status.getReason()); // now try to renew the canceled assertion. request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, null, null); RenewTargetType renewTarget = new RenewTargetType(); renewTarget.add(assertion); request.setRenewTarget(renewTarget); // we should receive an exception when renewing the token. try { this.tokenService.invoke(this.createSourceFromRequest(request)); fail("Renewing a canceled token should result in an exception being thrown"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains("has been canceled and cannot be renewed") == false) throw new RuntimeException("Unexpected exception message"); } } /** * <p> * This test tries to request a token of an unknown type, checking if an exception is correctly thrown by the * security token * service. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvokeUnknownTokenType() throws Exception { // create a simple token request, asking for an "unknown" test token. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, "http://www.tokens.org/UnknownToken", null); Source requestMessage = this.createSourceFromRequest(request); // invoke the security token service. try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { assertNotNull("Unexpected null cause", we.getCause()); assertTrue("Unexpected cause type", we.getCause() instanceof WSTrustException); } } /** * <p> * This test verifies if the token service is correctly identifying invalid issue requests. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvalidIssueRequests() throws Exception { // lets create an issue request that container neither an applies-to nor a token type. RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. A WSTrustException should be raised. try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { assertNotNull("Unexpected null cause", we.getCause()); assertTrue("Unexpected cause type", we.getCause() instanceof WSTrustException); /* * assertEquals("Either AppliesTo or TokenType must be present in a security token request", we.getCause() * .getMessage()); */ } // a request that asks for a public key to be used as proof key will fail if the public key is not available. request.setTokenType(URI.create(SAMLUtil.SAML2_TOKEN_TYPE)); request.setKeyType(URI.create(WSTrustConstants.KEY_TYPE_PUBLIC)); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains("Unable to locate client public key") == false) throw new RuntimeException("Unexpected exception message"); } } /** * <p> * This test verifies if the token service is correctly identifying invalid renew requests. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvalidRenewRequests() throws Exception { // first create a request that doesn't have a renew target element. RequestSecurityToken request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains("PL00092: Null Value:renew target") == false) throw new RuntimeException("Unexpected exception message"); } // a request with an empty renew target should also result in a failure. request.setRenewTarget(new RenewTargetType()); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof ParsingException); String msg = t.getMessage(); if (msg.contains("Unable to parse token request: security token is null") == false) throw new RuntimeException("Unexpected exception message"); } // a request to renew an unknown token (i.e. there's no provider can handle the token) should also fail. request.getRenewTarget().add(this.createUnknownToken()); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains(ErrorCodes.INVALID_DIGITAL_SIGNATURE) == false) throw new RuntimeException("Unexpected error msg"); } } /** * <p> * This test verifies if the token service is correctly identifying invalid validate requests. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvalidValidateRequests() throws Exception { // first create a request that doesn't have a validate target element. RequestSecurityToken request = this.createRequest("validatecontext", WSTrustConstants.VALIDATE_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains("request does not have a validate target. Unable to validate token") == false) throw new RuntimeException("Unexpected exception message"); } // a request with an empty validate target should also result in a failure. request.setValidateTarget(new ValidateTargetType()); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof ParsingException); String msg = t.getMessage(); if (msg.contains("Unable to parse token request: security token is null") == false) throw new RuntimeException("Unexpected exception message"); } // a request to validate an unknown token (i.e. there's no provider can handle the token) should also fail. request.getValidateTarget().add(this.createUnknownToken()); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); Source responseMessage = this.tokenService.invoke(requestMessage); RequestSecurityTokenResponseCollection baseResponseColl = (RequestSecurityTokenResponseCollection) new WSTrustParser() .parse(DocumentUtil.getSourceAsStream(responseMessage)); RequestSecurityTokenResponse response = baseResponseColl.getRequestSecurityTokenResponses().get(0); StatusType status = response.getStatus(); assertTrue(status.getCode().equals(WSTrustConstants.STATUS_CODE_INVALID)); // fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { assertNotNull("Unexpected null cause", we.getCause()); assertTrue("Unexpected cause type", we.getCause() instanceof WSTrustException); assertEquals("No SecurityTokenProvider configured for http://www.unknowntoken.org:UnknownToken", we.getCause() .getMessage()); } } /** * <p> * This test verifies if the token service is correctly identifying invalid cancel requests. * </p> * * @throws Exception if an error occurs while running the test. */ @Test public void testInvalidCancelRequests() throws Exception { // first create a request that doesn't have a cancel target element. RequestSecurityToken request = this.createRequest("cancelcontext", WSTrustConstants.CANCEL_REQUEST, SAMLUtil.SAML2_TOKEN_TYPE, null); Source requestMessage = this.createSourceFromRequest(request); // invoke the token service. try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains("request does not have a cancel target. Unable to cancel token") == false) throw new RuntimeException("Unexpected exception message"); } // a request with an empty cancel target should also result in a failure. request.setCancelTarget(new CancelTargetType()); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof ParsingException); String msg = t.getMessage(); if (msg.contains("Unable to parse token request: security token is null") == false) throw new RuntimeException("Unexpected exception message"); } // a request to cancel an unknown token (i.e. there's no provider can handle the token) should also fail. request.getCancelTarget().add(this.createUnknownToken()); requestMessage = this.createSourceFromRequest(request); try { this.tokenService.invoke(requestMessage); fail("An exception should have been raised by the security token service"); } catch (WebServiceException we) { Throwable t = we.getCause(); assertNotNull("Unexpected null cause", t); assertTrue("Unexpected cause type", t instanceof WSTrustException); String msg = t.getMessage(); if (msg.contains(ErrorCodes.STS_NO_TOKEN_PROVIDER) == false) throw new RuntimeException("Unexpected exception message"); } } /** * <p> * Validates the contents of a WS-Trust response message that contains a custom token issued by the test * {@code SpecialTokenProvider}. * </p> * * @param baseResponse a reference to the WS-Trust response that was sent by the STS. * * @throws Exception if one of the validation performed fail. */ private void validateCustomTokenResponse(BaseRequestSecurityTokenResponse baseResponse) throws Exception { // =============================== WS-Trust Security Token Response Validation ===============================// assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", "testcontext", response.getContext()); assertEquals("Unexpected token type", "http://www.tokens.org/SpecialToken", response.getTokenType().toString()); Lifetime lifetime = response.getLifetime(); assertNotNull("Unexpected null token lifetime", lifetime); // ========================================= Custom Token Validation =========================================// RequestedSecurityTokenType requestedToken = response.getRequestedSecurityToken(); assertNotNull("Unexpected null requested security token", requestedToken); Object token = requestedToken.getAny().get(0); assertNotNull("Unexpected null token", token); assertTrue("Unexpected token class", token instanceof Element); Element element = (Element) requestedToken.getAny().get(0); assertEquals("Unexpected root element name", "SpecialToken", element.getLocalName()); assertEquals("Unexpected namespace value", "http://www.tokens.org", element.getNamespaceURI()); assertEquals("Unexpected attribute value", "http://www.tokens.org/SpecialToken", element.getAttribute("TokenType")); element = (Element) element.getFirstChild(); assertEquals("Unexpected child element name", "SpecialTokenValue", element.getLocalName()); assertEquals("Unexpected token value", "Principal:jduke", element.getFirstChild().getNodeValue()); } private SAML11AssertionType validateSAML11AssertionResponse(BaseRequestSecurityTokenResponse baseResponse, String context, String principal, String confirmationMethod) throws Exception { // =============================== WS-Trust Security Token Response Validation ===============================// assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", context, response.getContext()); assertEquals("Unexpected token type", SAMLUtil.SAML11_TOKEN_TYPE, response.getTokenType().toString()); Lifetime lifetime = response.getLifetime(); assertNotNull("Unexpected null token lifetime", lifetime); // validate the attached token reference. RequestedReferenceType reference = response.getRequestedAttachedReference(); assertNotNull("Unexpected null attached reference", reference); SecurityTokenReferenceType securityRef = reference.getSecurityTokenReference(); assertNotNull("Unexpected null security reference", securityRef); String tokenTypeAttr = securityRef.getOtherAttributes().get(new QName(WSTrustConstants.WSSE11_NS, "TokenType")); assertNotNull("Required attribute TokenType is missing", tokenTypeAttr); assertEquals("TokenType attribute has an unexpected value", SAMLUtil.SAML11_TOKEN_TYPE, tokenTypeAttr); KeyIdentifierType keyId = (KeyIdentifierType) securityRef.getAny().get(0); assertEquals("Unexpected key value type", SAMLUtil.SAML11_VALUE_TYPE, keyId.getValueType()); assertNotNull("Unexpected null key identifier value", keyId.getValue()); // ====================================== SAMLV1.1 Assertion Validation ======================================// RequestedSecurityTokenType requestedToken = response.getRequestedSecurityToken(); assertNotNull("Unexpected null requested security token", requestedToken); // unmarshall the SAMLV1.1 assertion. Element assertionElement = (Element) requestedToken.getAny().get(0); SAML11AssertionType assertion = SAMLUtil.saml11FromElement(assertionElement); // verify the contents of the unmarshalled assertion. assertNotNull("Invalid null assertion ID", assertion.getID()); assertEquals(keyId.getValue().substring(1), assertion.getID()); assertEquals(lifetime.getCreated(), assertion.getIssueInstant()); assertEquals(1, assertion.getMajorVersion()); assertEquals(1, assertion.getMinorVersion()); // validate the assertion issuer. assertNotNull("Unexpected null assertion issuer", assertion.getIssuer()); assertEquals("Unexpected assertion issuer name", "Test STS", assertion.getIssuer()); // validate the assertion authentication statement. List<SAML11StatementAbstractType> statements = assertion.getStatements(); assertTrue("At least one statement is expected in a SAMLV1.1 assertion", statements.size() > 0); SAML11AuthenticationStatementType authStatement = null; for (SAML11StatementAbstractType statement : statements) { if (statement instanceof SAML11AuthenticationStatementType) { authStatement = (SAML11AuthenticationStatementType) statement; break; } } assertNotNull("SAMLV1.1 assertion is missing the authentication statement", authStatement); // validate the assertion subject. assertNotNull("Unexpected null subject", authStatement.getSubject()); SAML11SubjectType subject = authStatement.getSubject(); SAML11NameIdentifierType nameID = subject.getChoice().getNameID(); assertEquals("Unexpected NameIdentifier format", SAML11Constants.FORMAT_UNSPECIFIED, nameID.getFormat().toString()); assertEquals("Unexpected NameIdentifier value", principal, nameID.getValue()); SAML11SubjectConfirmationType subjType = subject.getSubjectConfirmation(); assertEquals("Unexpected confirmation method", confirmationMethod, subjType.getConfirmationMethod().get(0).toString()); // validate the assertion conditions. assertNotNull("Unexpected null conditions", assertion.getConditions()); assertEquals(lifetime.getCreated(), assertion.getConditions().getNotBefore()); assertEquals(lifetime.getExpires(), assertion.getConditions().getNotOnOrAfter()); assertNotNull("Assertion should have been signed", assertion.getSignature()); return assertion; } /** * <p> * Validates the contents of a WS-Trust response message that contains a SAMLV2.0 assertion issued by the * {@code SAML20TokenProvider}. * </p> * * @param baseResponse a reference to the WS-Trust response that was sent by the STS. * @param context the expected name of the response context. * @param principal the principal that is expected to be seen in the assertion subject. * @param confirmationMethod the confirmation method that is expected to be seen in the assertion subject. * * @return the SAMLV2.0 assertion that has been extracted from the response. This object can be used by the test * methods to * perform extra validations depending on the scenario being tested. * * @throws Exception if an error occurs while performing the validation. */ private AssertionType validateSAMLAssertionResponse(BaseRequestSecurityTokenResponse baseResponse, String context, String principal, String confirmationMethod) throws Exception { // =============================== WS-Trust Security Token Response Validation ===============================// assertNotNull("Unexpected null response", baseResponse); assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection); RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse; assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size()); RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0); assertEquals("Unexpected response context", context, response.getContext()); assertEquals("Unexpected token type", SAMLUtil.SAML2_TOKEN_TYPE, response.getTokenType().toString()); Lifetime lifetime = response.getLifetime(); assertNotNull("Unexpected null token lifetime", lifetime); // validate the attached token reference. RequestedReferenceType reference = response.getRequestedAttachedReference(); assertNotNull("Unexpected null attached reference", reference); SecurityTokenReferenceType securityRef = reference.getSecurityTokenReference(); assertNotNull("Unexpected null security reference", securityRef); String tokenTypeAttr = securityRef.getOtherAttributes().get(new QName(WSTrustConstants.WSSE11_NS, "TokenType")); assertNotNull("Required attribute TokenType is missing", tokenTypeAttr); assertEquals("TokenType attribute has an unexpected value", SAMLUtil.SAML2_TOKEN_TYPE, tokenTypeAttr); KeyIdentifierType keyId = (KeyIdentifierType) securityRef.getAny().get(0); assertEquals("Unexpected key value type", SAMLUtil.SAML2_VALUE_TYPE, keyId.getValueType()); assertNotNull("Unexpected null key identifier value", keyId.getValue()); // ====================================== SAMLV2.0 Assertion Validation ======================================// RequestedSecurityTokenType requestedToken = response.getRequestedSecurityToken(); assertNotNull("Unexpected null requested security token", requestedToken); // unmarshall the SAMLV2.0 assertion. Element assertionElement = (Element) requestedToken.getAny().get(0); AssertionType assertion = SAMLUtil.fromElement(assertionElement); // verify the contents of the unmarshalled assertion. assertNotNull("Invalid null assertion ID", assertion.getID()); assertEquals(keyId.getValue().substring(1), assertion.getID()); assertEquals(lifetime.getCreated(), assertion.getIssueInstant()); // validate the assertion issuer. assertNotNull("Unexpected null assertion issuer", assertion.getIssuer()); assertEquals("Unexpected assertion issuer name", "Test STS", assertion.getIssuer().getValue()); // validate the assertion subject. assertNotNull("Unexpected null subject", assertion.getSubject()); SubjectType subject = assertion.getSubject(); NameIDType nameID = (NameIDType) subject.getSubType().getBaseID(); assertEquals("Unexpected name id qualifier", "urn:picketlink:identity-federation", nameID.getNameQualifier()); assertEquals("Unexpected name id value", principal, nameID.getValue()); SubjectConfirmationType subjType = subject.getConfirmation().get(0); assertEquals("Unexpected confirmation method", confirmationMethod, subjType.getMethod()); // validate the assertion conditions. assertNotNull("Unexpected null conditions", assertion.getConditions()); assertEquals(lifetime.getCreated(), assertion.getConditions().getNotBefore()); assertEquals(lifetime.getExpires(), assertion.getConditions().getNotOnOrAfter()); assertNotNull("Assertion should have been signed", assertion.getSignature()); return assertion; } /** * <p> * Validates the contents of the specified {@code SubjectConfirmationType} when the {@code HOLDER_OF_KEY} * confirmation * method has been used. * </p> * * @param subjectConfirmation the {@code SubjectConfirmationType} to be validated. * @param keyType the type of the proof-of-possession key (Symmetric or Public). * @param certificate the certificate used in the Public Key scenarios. * @param usePublicKey {@code true} if the certificate's Public Key was used as the proof-of-possession token; * {@code false} * otherwise. * * @throws Exception if an error occurs while performing the validation. */ private void validateHolderOfKeyContents(SubjectConfirmationType subjectConfirmation, String keyType, Certificate certificate, boolean usePublicKey) throws Exception { SubjectConfirmationDataType subjConfirmationDataType = subjectConfirmation.getSubjectConfirmationData(); assertNotNull("Unexpected null subject confirmation data", subjConfirmationDataType); KeyInfoType keyInfo = (KeyInfoType) subjConfirmationDataType.getAnyType(); assertEquals("Unexpected key info content size", 1, keyInfo.getContent().size()); // if the key is a symmetric key, the KeyInfo should contain an encrypted element. if (WSTrustConstants.KEY_TYPE_SYMMETRIC.equals(keyType)) { Element encKeyElement = (Element) keyInfo.getContent().get(0); assertEquals("Unexpected key info content type", WSTrustConstants.XMLEnc.ENCRYPTED_KEY, encKeyElement.getLocalName()); } // if the key is public, KeyInfo should either contain an encoded certificate or an encoded public key. else if (WSTrustConstants.KEY_TYPE_PUBLIC.equals(keyType)) { // if the public key has been used as proof, we should be able to retrieve it from KeyValueType. if (usePublicKey == true) { KeyValueType keyValue = (KeyValueType) keyInfo.getContent().get(0); RSAKeyValueType rsaKeyValue = (RSAKeyValueType) keyValue; // reconstruct the public key and check if it matches the public key of the provided certificate. BigInteger modulus = new BigInteger(1, Base64.decode(new String(rsaKeyValue.getModulus()))); BigInteger exponent = new BigInteger(1, Base64.decode(new String(rsaKeyValue.getExponent()))); KeyFactory factory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); RSAPublicKey genKey = (RSAPublicKey) factory.generatePublic(spec); assertEquals("Invalid public key", certificate.getPublicKey(), genKey); } // if the whole certificate was used as proof, we should be able to retrieve it from X509DataType. else { X509DataType x509Data = (X509DataType) keyInfo.getContent().get(0); assertEquals("Unexpected X509 data content size", 1, x509Data.getDataObjects().size()); Object content = x509Data.getDataObjects().get(0); assertTrue("Unexpected X509 data content type", content instanceof X509CertificateType); byte[] encodedCertificate = ((X509CertificateType) content).getEncodedCertificate(); // reconstruct the certificate and check if it matches the provided certificate. ByteArrayInputStream byteInputStream = new ByteArrayInputStream(Base64.decode(encodedCertificate, 0, encodedCertificate.length)); assertEquals("Invalid certificate in key info", certificate, CertificateFactory.getInstance("X.509") .generateCertificate(byteInputStream)); } } } /** * <p> * Utility method that creates a simple WS-Trust request using the specified information. * </p> * * @param context a {@code String} that represents the request context. * @param requestType a {@code String} that represents the WS-Trust request type. * @param tokenType a {@code String} that represents the requested token type. * @param appliesToString a {@code String} that represents the URL of a service provider. * * @return the constructed {@code RequestSecurityToken} object. */ private RequestSecurityToken createRequest(String context, String requestType, String tokenType, String appliesToString) { RequestSecurityToken request = new RequestSecurityToken(); request.setContext(context); request.setRequestType(URI.create(requestType)); if (tokenType != null) request.setTokenType(URI.create(tokenType)); if (appliesToString != null) request.setAppliesTo(WSTrustUtil.createAppliesTo(appliesToString)); return request; } /** * <p> * Creates a simple token that is not known to the STS for testing purposes. * </p> * * @return an {@code Element} representing the unknown token. * * @throws Exception if an error occurs while creating the token. */ private Element createUnknownToken() throws Exception { Document doc = DocumentUtil.createDocument(); String namespaceURI = "http://www.unknowntoken.org"; Element root = doc.createElementNS(namespaceURI, "token:UnknownToken"); Element child = doc.createElementNS(namespaceURI, "token:UnknownTokenValue"); child.appendChild(doc.createTextNode("Unknown content")); root.appendChild(child); String id = IDGenerator.create("ID_"); root.setAttributeNS(namespaceURI, "ID", id); root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:token", namespaceURI); return root; } /** * <p> * Obtains the {@code Certificate} stored under the specified alias in the specified keystore. * </p> * * @param keyStoreFile the name of the file that contains a JKS keystore. * @param passwd the keystore password. * @param certificateAlias the alias of a certificate in the keystore. * * @return a reference to the {@code Certificate} stored under the given alias. * * @throws Exception if an error occurs while handling the keystore. */ private Certificate getCertificate(String keyStoreFile, String passwd, String certificateAlias) throws Exception { InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(keyStoreFile); KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(stream, passwd.toCharArray()); Certificate certificate = keyStore.getCertificate(certificateAlias); return certificate; } private Source createSourceFromRequest(RequestSecurityToken request) throws Exception { // write the request XML to a DOMResult DOMResult result = new DOMResult(DocumentUtil.createDocument()); WSTrustRequestWriter writer = new WSTrustRequestWriter(result); writer.write(request); return new DOMSource(result.getNode()); } /** * <p> * Helper class that exposes the PicketLinkSTS methods as public for the tests to work. * </p> * * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a> */ class TestSTS extends PicketLinkSTS { private String configFileName = "sts/picketlink-sts.xml"; private boolean soap12 = false; TestSTS() { } TestSTS(String configFileName) { this.configFileName = configFileName; } TestSTS(String configFileName, boolean useSOAP12) { this.configFileName = configFileName; this.soap12 = useSOAP12; } public Source invoke(Source source) { try { SOAPMessage request = null; if (soap12) { request = SOAPUtil.createSOAP12(); } else { request = SOAPUtil.create(); } SOAPUtil.addData(source, request); SOAPMessage response = super.invoke(request); return new DOMSource(SOAPUtil.getSOAPData(response)); } catch (SOAPException e) { throw new RuntimeException(e); } } @Override public STSConfiguration getConfiguration() throws ConfigurationException { InputStream stream; try { URL configURL = Thread.currentThread().getContextClassLoader().getResource(configFileName); stream = configURL.openStream(); STSType stsConfig = (STSType) new STSConfigParser().parse(stream); return new PicketLinkSTSConfiguration(stsConfig); } catch (Exception e) { throw new RuntimeException(e); } } public void setContext(WebServiceContext context) { super.context = context; } public void setSoap12(boolean soap12) { this.soap12 = soap12; } } /** * <p> * Helper class that mocks a {@code WebServiceContext}. It is used in the PicketLink STS test cases. * </p> * * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a> */ class TestContext implements WebServiceContext { private Principal principal; /* * (non-Javadoc) * * @see javax.xml.ws.WebServiceContext#getEndpointReference(java.lang.Class, org.w3c.dom.Element[]) */ public <T extends EndpointReference> T getEndpointReference(Class<T> arg0, Element... arg1) { return null; } /* * (non-Javadoc) * * @see javax.xml.ws.WebServiceContext#getEndpointReference(org.w3c.dom.Element[]) */ public EndpointReference getEndpointReference(Element... arg0) { return null; } /* * (non-Javadoc) * * @see javax.xml.ws.WebServiceContext#getMessageContext() */ public MessageContext getMessageContext() { return null; } /* * (non-Javadoc) * * @see javax.xml.ws.WebServiceContext#getUserPrincipal() */ public Principal getUserPrincipal() { return this.principal; } /** * <p> * Sets the principal to be used in the test case. * </p> * * @param principal the {@code Principal} to be set. */ public void setUserPrincipal(Principal principal) { this.principal = principal; } /* * (non-Javadoc) * * @see javax.xml.ws.WebServiceContext#isUserInRole(java.lang.String) */ public boolean isUserInRole(String arg0) { return false; } } }