/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.support.wss.entries;
import com.eviware.soapui.config.WSSEntryConfig;
import com.eviware.soapui.impl.wsdl.support.wss.OutgoingWss;
import com.eviware.soapui.impl.wsdl.support.wss.WssContainer;
import com.eviware.soapui.impl.wsdl.support.wss.WssCrypto;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContext;
import com.eviware.soapui.support.types.StringToStringMap;
import com.eviware.soapui.support.xml.XmlUtils;
import com.eviware.soapui.utils.TestUtils;
import org.apache.ws.commons.util.NamespaceContextImpl;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.components.crypto.CredentialException;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.saml.ext.builder.SAML1Constants;
import org.apache.ws.security.saml.ext.builder.SAML2Constants;
import org.apache.ws.security.util.Loader;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
import org.apache.xmlbeans.XmlObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.w3c.dom.Document;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
/**
* @author Erik R. Yverling
*/
//FIXME Use the *-test keys instead of the keystore.jks
// TODO Add test for invalid SAML version
public class AutomaticSAMLEntryTest {
private static final String ISSUER = "www.issuer.com";
private static final String KEYSTORE_PATH = "keys/keystore.jks";
private static final String KEYSTORE_PASSWORD = "foobar42";
private static final String KEY_PASSWORD = "foobar42";
private static final String ALIAS = "certificatekey";
private static final String SUBJECT_QUALIFIER = "www.subject.com";
private static final String SUBJECT_NAME = "uid=joe,ou=people,ou=saml-demo,o=example.com";
private static final String ATTTRIBUTE_NAME = "attibuteName";
private static final String ATTTRIBUTE_VALUE = "attributeValue";
private AutomaticSAMLEntry automaticSamlEntry;
private Merlin crypto;
private Document doc;
private WSSecHeader secHeader;
private XPathFactory factory;
private XPath xpath;
@Mock
private PropertyExpansionContext contextMock;
@Mock
private OutgoingWss outgoingWssMock;
@Mock
private WSSEntryConfig wssEntryConfigMock;
@Mock
private WssContainer wssContainerMock;
@Mock
private WssCrypto wssCryptoMock;
@Mock
private XmlObject xmlObjectMock;
private NamespaceContextImpl namespaceContext;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
initXpath();
doc = XmlUtils.parseXml(TestUtils.SAMPLE_SOAP_MESSAGE);
secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);
automaticSamlEntry = new AutomaticSAMLEntry();
automaticSamlEntry.init(wssEntryConfigMock, outgoingWssMock);
when(wssEntryConfigMock.getConfiguration()).thenReturn(xmlObjectMock);
automaticSamlEntry.setIssuer(ISSUER);
automaticSamlEntry.setSubjectQualifier(SUBJECT_QUALIFIER);
automaticSamlEntry.setSubjectName(SUBJECT_NAME);
automaticSamlEntry.setSignatureAlgorithm(WSConstants.RSA);
automaticSamlEntry.setDigestAlgorithm(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA1);
createCrypto();
when(outgoingWssMock.getWssContainer()).thenReturn(wssContainerMock);
when(wssContainerMock.getCryptoByName(anyString(), anyBoolean())).thenReturn(wssCryptoMock);
when(wssContainerMock.getCryptoByName(anyString())).thenReturn(wssCryptoMock);
when(wssCryptoMock.getCrypto()).thenReturn(crypto);
when(outgoingWssMock.getUsername()).thenReturn(ALIAS);
when(outgoingWssMock.getPassword()).thenReturn(KEY_PASSWORD);
when(contextMock.expand(ALIAS)).thenReturn(ALIAS);
when(contextMock.expand(KEY_PASSWORD)).thenReturn(KEY_PASSWORD);
when(contextMock.expand(ISSUER)).thenReturn(ISSUER);
when(contextMock.expand(SUBJECT_NAME)).thenReturn(SUBJECT_NAME);
when(contextMock.expand(SUBJECT_QUALIFIER)).thenReturn(SUBJECT_QUALIFIER);
when(contextMock.expand(ATTTRIBUTE_NAME)).thenReturn(ATTTRIBUTE_NAME);
}
@Test
public void testProcessUnsignedSAML1AuthenticationAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_1, false, AutomaticSAMLEntry.AUTHENTICATION_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertEquals(xpath.evaluate("//saml1:ConfirmationMethod", doc, XPathConstants.STRING),
SAML1Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml1:AuthenticationStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessUnsignedSAML2AuthenticationAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_2, false, AutomaticSAMLEntry.AUTHENTICATION_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertEquals(xpath.evaluate("//saml2:SubjectConfirmation/@Method", doc, XPathConstants.STRING),
SAML2Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml2:AuthnStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessUnsignedSAML1AttributeAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_1, false, AutomaticSAMLEntry.ATTRIBUTE_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
createAttribute();
automaticSamlEntry.process(secHeader, doc, contextMock);
System.out.println(XmlUtils.serializePretty(doc));
assertEquals(xpath.evaluate("//saml1:ConfirmationMethod", doc, XPathConstants.STRING),
SAML1Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml1:AttributeStatement", doc, XPathConstants.NODE));
assertEquals(
xpath.evaluate("//saml1:AttributeStatement/saml1:Attribute/@AttributeName", doc, XPathConstants.STRING),
ATTTRIBUTE_NAME);
assertEquals(xpath.evaluate("//saml1:AttributeStatement/saml1:Attribute/saml1:AttributeValue", doc,
XPathConstants.STRING), ATTTRIBUTE_VALUE);
}
@Test
public void testProcessUnsignedSAML2AttributeAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_2, false, AutomaticSAMLEntry.ATTRIBUTE_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
createAttribute();
automaticSamlEntry.process(secHeader, doc, contextMock);
System.out.println(XmlUtils.serializePretty(doc));
assertEquals(xpath.evaluate("//saml2:SubjectConfirmation/@Method", doc, XPathConstants.STRING),
SAML2Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml2:AttributeStatement", doc, XPathConstants.NODE));
assertEquals(
xpath.evaluate("//saml2:AttributeStatement/saml2:Attribute/@FriendlyName", doc, XPathConstants.STRING),
ATTTRIBUTE_NAME);
assertEquals(xpath.evaluate("//saml2:AttributeStatement/saml2:Attribute/saml2:AttributeValue", doc,
XPathConstants.STRING), ATTTRIBUTE_VALUE);
}
@Test
public void testProcessUnsignedSAML1AuthorizationAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_1, false, AutomaticSAMLEntry.AUTHORIZATION_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertEquals(xpath.evaluate("//saml1:ConfirmationMethod", doc, XPathConstants.STRING),
SAML1Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml1:AuthorizationDecisionStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessUnsignedSAML2AuthorizationAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_2, false, AutomaticSAMLEntry.AUTHORIZATION_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertEquals(xpath.evaluate("//saml2:SubjectConfirmation/@Method", doc, XPathConstants.STRING),
SAML2Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml2:AuthzDecisionStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessSignedSAML1AuthenticationAssertionUsingHolderOfKey() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_1, true, AutomaticSAMLEntry.AUTHENTICATION_ASSERTION_TYPE,
AutomaticSAMLEntry.HOLDER_OF_KEY_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
System.err.println(XmlUtils.serializePretty(doc));
assertNotNull(xpath.evaluate("//ds:Signature", doc, XPathConstants.NODE));
assertEquals(xpath.evaluate("//saml1:ConfirmationMethod", doc, XPathConstants.STRING),
SAML1Constants.CONF_HOLDER_KEY);
assertNotNull(xpath.evaluate("//saml1:AuthenticationStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessSignedSAML2AuthenticationAssertionUsingHolderOfKey() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_2, true, AutomaticSAMLEntry.AUTHENTICATION_ASSERTION_TYPE,
AutomaticSAMLEntry.HOLDER_OF_KEY_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//ds:Signature", doc, XPathConstants.NODE));
assertEquals(xpath.evaluate("//saml2:SubjectConfirmation/@Method", doc, XPathConstants.STRING),
SAML2Constants.CONF_HOLDER_KEY);
assertNotNull(xpath.evaluate("//saml2:AuthnStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessSignedSAML1AttributeAssertionUsingHolderOfKey() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_1, true, AutomaticSAMLEntry.ATTRIBUTE_ASSERTION_TYPE,
AutomaticSAMLEntry.HOLDER_OF_KEY_CONFIRMATION_METHOD);
createAttribute();
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//ds:Signature", doc, XPathConstants.NODE));
assertEquals(xpath.evaluate("//saml1:ConfirmationMethod", doc, XPathConstants.STRING),
SAML1Constants.CONF_HOLDER_KEY);
assertNotNull(xpath.evaluate("//saml1:AttributeStatement", doc, XPathConstants.NODE));
assertEquals(
xpath.evaluate("//saml1:AttributeStatement/saml1:Attribute/@AttributeName", doc, XPathConstants.STRING),
ATTTRIBUTE_NAME);
assertEquals(xpath.evaluate("//saml1:AttributeStatement/saml1:Attribute/saml1:AttributeValue", doc,
XPathConstants.STRING), ATTTRIBUTE_VALUE);
}
@Test
public void testProcessSignedSAML2AttributeAssertionUsingHolderOfKey() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_2, true, AutomaticSAMLEntry.ATTRIBUTE_ASSERTION_TYPE,
AutomaticSAMLEntry.HOLDER_OF_KEY_CONFIRMATION_METHOD);
createAttribute();
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//ds:Signature", doc, XPathConstants.NODE));
assertEquals(xpath.evaluate("//saml2:SubjectConfirmation/@Method", doc, XPathConstants.STRING),
SAML2Constants.CONF_HOLDER_KEY);
assertNotNull(xpath.evaluate("//saml2:AttributeStatement", doc, XPathConstants.NODE));
assertEquals(
xpath.evaluate("//saml2:AttributeStatement/saml2:Attribute/@FriendlyName", doc, XPathConstants.STRING),
ATTTRIBUTE_NAME);
assertEquals(xpath.evaluate("//saml2:AttributeStatement/saml2:Attribute/saml2:AttributeValue", doc,
XPathConstants.STRING), ATTTRIBUTE_VALUE);
}
@Test
public void testProcessSignedSAML1AuthenticationAssertionUsingSenderVouces() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_1, true, AutomaticSAMLEntry.AUTHENTICATION_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//ds:Signature", doc, XPathConstants.NODE));
assertEquals(xpath.evaluate("//saml1:ConfirmationMethod", doc, XPathConstants.STRING),
SAML1Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml1:AuthenticationStatement", doc, XPathConstants.NODE));
}
@Test
public void testProcessSignedSAML2AuthenticationAssertionUsingSenderVouches() throws WSSecurityException,
XPathExpressionException {
setRequiredFields(AutomaticSAMLEntry.SAML_VERSION_2, true, AutomaticSAMLEntry.AUTHENTICATION_ASSERTION_TYPE,
AutomaticSAMLEntry.SENDER_VOUCHES_CONFIRMATION_METHOD);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//ds:Signature", doc, XPathConstants.NODE));
assertEquals(xpath.evaluate("//saml2:SubjectConfirmation/@Method", doc, XPathConstants.STRING),
SAML2Constants.CONF_SENDER_VOUCHES);
assertNotNull(xpath.evaluate("//saml2:AuthnStatement", doc, XPathConstants.NODE));
}
@Test
public void testUserInputFieldsForSAML1() throws WSSecurityException, XPathExpressionException {
automaticSamlEntry.setSamlVersion(AutomaticSAMLEntry.SAML_VERSION_1);
automaticSamlEntry.setSigned(true);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//saml1:Assertion", doc, XPathConstants.NODE));
assertNotNull(xpath.evaluate("//saml1:Assertion/@Issuer", doc, XPathConstants.NODE));
assertNotNull(xpath.evaluate("//saml1:NameIdentifier", doc, XPathConstants.NODE));
assertNotNull(xpath.evaluate("//saml1:NameIdentifier/@NameQualifier", doc, XPathConstants.NODE));
}
@Test
public void testUserInputFieldsForSAML2() throws WSSecurityException, XPathExpressionException {
automaticSamlEntry.setSamlVersion(AutomaticSAMLEntry.SAML_VERSION_2);
automaticSamlEntry.setSigned(true);
automaticSamlEntry.process(secHeader, doc, contextMock);
assertNotNull(xpath.evaluate("//saml2:Assertion", doc, XPathConstants.NODE));
assertNotNull(xpath.evaluate("//saml2:Issuer", doc, XPathConstants.NODE));
assertNotNull(xpath.evaluate("//saml2:NameID", doc, XPathConstants.NODE));
assertNotNull(xpath.evaluate("//saml2:NameID/@NameQualifier", doc, XPathConstants.NODE));
}
private void initXpath() {
factory = XPathFactory.newInstance();
namespaceContext = new NamespaceContextImpl();
namespaceContext.startPrefixMapping("saml1", "urn:oasis:names:tc:SAML:1.0:assertion");
namespaceContext.startPrefixMapping("saml2", "urn:oasis:names:tc:SAML:2.0:assertion");
namespaceContext.startPrefixMapping("ds", "http://www.w3.org/2000/09/xmldsig#");
xpath = factory.newXPath();
xpath.setNamespaceContext(namespaceContext);
}
private void createCrypto() throws KeyStoreException, CredentialException, IOException, NoSuchAlgorithmException,
CertificateException {
WSSConfig.init();
crypto = new Merlin();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
ClassLoader loader = Loader.getClassLoader(AutomaticSAMLEntryTest.class);
InputStream input = Merlin.loadInputStream(loader, KEYSTORE_PATH);
keyStore.load(input, KEYSTORE_PASSWORD.toCharArray());
((Merlin) crypto).setKeyStore(keyStore);
}
private void setRequiredFields(String version, boolean signed, String assertionType, String confirmationMethod) {
automaticSamlEntry.setSamlVersion(version);
automaticSamlEntry.setSigned(signed);
automaticSamlEntry.setAssertionType(assertionType);
automaticSamlEntry.setConfirmationMethod(confirmationMethod);
}
private void createAttribute() {
automaticSamlEntry.setAttributeName(ATTTRIBUTE_NAME);
StringToStringMap attributeValueRow = new StringToStringMap();
attributeValueRow.put(AutomaticSAMLEntry.ATTRIBUTE_VALUES_VALUE_COLUMN, ATTTRIBUTE_VALUE);
automaticSamlEntry.setAttributeValues(Collections.singletonList(attributeValueRow));
}
}