Skip to content

Commit 845eccb

Browse files
committed
Adding SAML SSO tests.
1 parent b32c5d0 commit 845eccb

File tree

3 files changed

+226
-5
lines changed

3 files changed

+226
-5
lines changed

Diff for: rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public SSOValidatorResponse validateSamlResponse(
9292
}
9393

9494
// Validate Assertions
95-
boolean foundValidSubject = false;
95+
org.opensaml.saml.saml2.core.Assertion validAssertion = null;
9696
Date sessionNotOnOrAfter = null;
9797
for (org.opensaml.saml.saml2.core.Assertion assertion : samlResponse.getAssertions()) {
9898
// Check the Issuer
@@ -114,7 +114,7 @@ public SSOValidatorResponse validateSamlResponse(
114114
org.opensaml.saml.saml2.core.Subject subject = assertion.getSubject();
115115
if (validateAuthenticationSubject(subject, assertion.getID(), postBinding)) {
116116
validateAudienceRestrictionCondition(assertion.getConditions());
117-
foundValidSubject = true;
117+
validAssertion = assertion;
118118
// Store Session NotOnOrAfter
119119
for (AuthnStatement authnStatment : assertion.getAuthnStatements()) {
120120
if (authnStatment.getSessionNotOnOrAfter() != null) {
@@ -123,10 +123,9 @@ public SSOValidatorResponse validateSamlResponse(
123123
}
124124
}
125125
}
126-
127126
}
128127

129-
if (!foundValidSubject) {
128+
if (validAssertion == null) {
130129
LOG.fine("The Response did not contain any Authentication Statement that matched "
131130
+ "the Subject Confirmation criteria");
132131
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
@@ -140,7 +139,7 @@ public SSOValidatorResponse validateSamlResponse(
140139
}
141140

142141
// the assumption for now is that SAMLResponse will contain only a single assertion
143-
Element assertionElement = samlResponse.getAssertions().get(0).getDOM();
142+
Element assertionElement = validAssertion.getDOM();
144143
Element clonedAssertionElement = (Element)assertionElement.cloneNode(true);
145144
validatorResponse.setAssertionElement(clonedAssertionElement);
146145
validatorResponse.setAssertion(DOM2Writer.nodeToString(clonedAssertionElement));

Diff for: rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java

+4
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public void setSubjectLocality(String ipAddress, String dnsAddress) {
130130
this.subjectLocalityDnsAddress = dnsAddress;
131131
}
132132

133+
public void setSubjectName(String subjectName) {
134+
this.subjectName = subjectName;
135+
}
136+
133137
public void setResource(String resource) {
134138
this.resource = resource;
135139
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cxf.rs.security.saml.sso;
21+
22+
import java.io.InputStream;
23+
import java.security.KeyStore;
24+
import java.util.Collections;
25+
26+
import javax.xml.parsers.DocumentBuilder;
27+
import javax.xml.parsers.DocumentBuilderFactory;
28+
29+
import org.w3c.dom.Document;
30+
import org.w3c.dom.Element;
31+
32+
import org.apache.wss4j.common.crypto.Crypto;
33+
import org.apache.wss4j.common.crypto.Merlin;
34+
import org.apache.wss4j.common.saml.OpenSAMLUtil;
35+
import org.apache.wss4j.common.saml.SAMLCallback;
36+
import org.apache.wss4j.common.saml.SAMLUtil;
37+
import org.apache.wss4j.common.saml.SamlAssertionWrapper;
38+
import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
39+
import org.apache.wss4j.common.saml.bean.ConditionsBean;
40+
import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
41+
import org.apache.wss4j.common.saml.builder.SAML2Constants;
42+
import org.apache.wss4j.common.util.Loader;
43+
import org.apache.wss4j.dom.WSConstants;
44+
import org.apache.wss4j.dom.WSSConfig;
45+
import org.joda.time.DateTime;
46+
import org.opensaml.saml.common.xml.SAMLConstants;
47+
import org.opensaml.saml.saml2.core.Response;
48+
import org.opensaml.saml.saml2.core.Status;
49+
50+
/**
51+
* Some unit tests for the SAMLProtocolResponseValidator and the SAMLSSOResponseValidator
52+
*/
53+
public class CombinedValidatorTest extends org.junit.Assert {
54+
55+
static {
56+
WSSConfig.init();
57+
OpenSAMLUtil.initSamlEngine();
58+
}
59+
60+
@org.junit.Test
61+
public void testSuccessfulValidation() throws Exception {
62+
63+
Element responseElement = createResponse();
64+
Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
65+
66+
Crypto issuerCrypto = new Merlin();
67+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
68+
ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
69+
InputStream input = Merlin.loadInputStream(loader, "alice.jks");
70+
keyStore.load(input, "password".toCharArray());
71+
((Merlin)issuerCrypto).setKeyStore(keyStore);
72+
73+
// Validate the Response
74+
SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
75+
validator.validateSamlResponse(
76+
marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
77+
);
78+
79+
// Test SSO validation
80+
SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
81+
ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
82+
ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
83+
ssoValidator.setClientAddress("http://apache.org");
84+
ssoValidator.setRequestId("12345");
85+
ssoValidator.setSpIdentifier("http://service.apache.org");
86+
87+
// Parse the response
88+
SSOValidatorResponse ssoResponse =
89+
ssoValidator.validateSamlResponse(marshalledResponse, false);
90+
SamlAssertionWrapper parsedAssertion =
91+
new SamlAssertionWrapper(ssoResponse.getAssertionElement());
92+
93+
assertEquals("alice", parsedAssertion.getSubjectName());
94+
}
95+
96+
@org.junit.Test
97+
public void testWrappingAttack3() throws Exception {
98+
Element responseElement = createResponse();
99+
100+
// Get Assertion Element
101+
Element assertionElement =
102+
(Element)responseElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion").item(0);
103+
assertNotNull(assertionElement);
104+
105+
// Clone it, strip the Signature, modify the Subject, change Subj Conf
106+
Element clonedAssertion = (Element)assertionElement.cloneNode(true);
107+
clonedAssertion.setAttributeNS(null, "ID", "_12345623562");
108+
Element sigElement =
109+
(Element)clonedAssertion.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature").item(0);
110+
clonedAssertion.removeChild(sigElement);
111+
112+
Element subjElement =
113+
(Element)clonedAssertion.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Subject").item(0);
114+
Element subjNameIdElement =
115+
(Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "NameID").item(0);
116+
subjNameIdElement.setTextContent("bob");
117+
118+
Element subjConfElement =
119+
(Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "SubjectConfirmation").item(0);
120+
subjConfElement.setAttributeNS(null, "Method", SAML2Constants.CONF_SENDER_VOUCHES);
121+
122+
// Now insert the modified cloned Assertion into the Response before actual assertion
123+
responseElement.insertBefore(clonedAssertion, assertionElement);
124+
125+
// System.out.println(DOM2Writer.nodeToString(responseElement));
126+
127+
Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
128+
129+
Crypto issuerCrypto = new Merlin();
130+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
131+
ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
132+
InputStream input = Merlin.loadInputStream(loader, "alice.jks");
133+
keyStore.load(input, "password".toCharArray());
134+
((Merlin)issuerCrypto).setKeyStore(keyStore);
135+
136+
// Validate the Response
137+
SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
138+
validator.validateSamlResponse(
139+
marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
140+
);
141+
142+
// Test SSO validation
143+
SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
144+
ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
145+
ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
146+
ssoValidator.setClientAddress("http://apache.org");
147+
ssoValidator.setRequestId("12345");
148+
ssoValidator.setSpIdentifier("http://service.apache.org");
149+
150+
// Parse the response
151+
SSOValidatorResponse ssoResponse =
152+
ssoValidator.validateSamlResponse(marshalledResponse, false);
153+
SamlAssertionWrapper parsedAssertion =
154+
new SamlAssertionWrapper(ssoResponse.getAssertionElement());
155+
156+
assertEquals("alice", parsedAssertion.getSubjectName());
157+
}
158+
159+
private Element createResponse() throws Exception {
160+
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
161+
docBuilderFactory.setNamespaceAware(true);
162+
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
163+
Document doc = docBuilder.newDocument();
164+
165+
Status status =
166+
SAML2PResponseComponentBuilder.createStatus(
167+
SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
168+
);
169+
Response response =
170+
SAML2PResponseComponentBuilder.createSAMLResponse(
171+
"http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
172+
);
173+
174+
// Create an AuthenticationAssertion
175+
SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
176+
callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
177+
callbackHandler.setIssuer("http://cxf.apache.org/issuer");
178+
callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
179+
callbackHandler.setSubjectName("alice");
180+
181+
SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
182+
subjectConfirmationData.setAddress("http://apache.org");
183+
subjectConfirmationData.setInResponseTo("12345");
184+
subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
185+
subjectConfirmationData.setRecipient("http://recipient.apache.org");
186+
callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
187+
188+
ConditionsBean conditions = new ConditionsBean();
189+
conditions.setNotBefore(new DateTime());
190+
conditions.setNotAfter(new DateTime().plusMinutes(5));
191+
192+
AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
193+
audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
194+
conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
195+
callbackHandler.setConditions(conditions);
196+
197+
SAMLCallback samlCallback = new SAMLCallback();
198+
SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
199+
SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
200+
201+
Crypto issuerCrypto = new Merlin();
202+
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
203+
ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
204+
InputStream input = Merlin.loadInputStream(loader, "alice.jks");
205+
keyStore.load(input, "password".toCharArray());
206+
((Merlin)issuerCrypto).setKeyStore(keyStore);
207+
208+
assertion.signAssertion("alice", "password", issuerCrypto, false);
209+
210+
response.getAssertions().add(assertion.getSaml2());
211+
212+
Element policyElement = OpenSAMLUtil.toDom(response, doc);
213+
doc.appendChild(policyElement);
214+
assertNotNull(policyElement);
215+
216+
return policyElement;
217+
}
218+
}

0 commit comments

Comments
 (0)