You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by ow...@apache.org on 2012/06/03 22:25:35 UTC
svn commit: r1345768 [1/2] - in /cxf/fediz/trunk: ./
examples/simpleWebapp/src/main/webapp/META-INF/
examples/wsclientWebapp/webapp/
examples/wsclientWebapp/webapp/src/main/webapp/META-INF/
plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/ plu...
Author: owulff
Date: Sun Jun 3 20:25:34 2012
New Revision: 1345768
URL: http://svn.apache.org/viewvc?rev=1345768&view=rev
Log:
Support several trusted issuer, certstores, testcases refactored
Added:
cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SamlAssertionValidator.java
cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/common/STSUtil.java
cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java
cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/KeystoreCallbackHandler.java
cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/SAML1CallbackHandler.java
cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/SAML2CallbackHandler.java
cxf/fediz/trunk/plugins/core/src/test/resources/stsrealm_a.jks (with props)
cxf/fediz/trunk/plugins/core/src/test/resources/stsrealm_b.jks (with props)
Removed:
cxf/fediz/trunk/plugins/core/src/test/resources/RSTR.xml
cxf/fediz/trunk/plugins/core/src/test/resources/RSTR_old.xml
cxf/fediz/trunk/plugins/core/src/test/resources/RSTR_replay.xml
cxf/fediz/trunk/plugins/core/src/test/resources/fediz_test_config2.xml
Modified:
cxf/fediz/trunk/examples/simpleWebapp/src/main/webapp/META-INF/context.xml
cxf/fediz/trunk/examples/wsclientWebapp/webapp/pom.xml
cxf/fediz/trunk/examples/wsclientWebapp/webapp/src/main/webapp/META-INF/context.xml
cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SAMLTokenValidator.java
cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/FederationProcessorTest.java
cxf/fediz/trunk/plugins/core/src/test/resources/fediz_test_config.xml
cxf/fediz/trunk/plugins/core/src/test/resources/signature.properties
cxf/fediz/trunk/pom.xml
Modified: cxf/fediz/trunk/examples/simpleWebapp/src/main/webapp/META-INF/context.xml
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/examples/simpleWebapp/src/main/webapp/META-INF/context.xml?rev=1345768&r1=1345767&r2=1345768&view=diff
==============================================================================
--- cxf/fediz/trunk/examples/simpleWebapp/src/main/webapp/META-INF/context.xml (original)
+++ cxf/fediz/trunk/examples/simpleWebapp/src/main/webapp/META-INF/context.xml Sun Jun 3 20:25:34 2012
@@ -1,5 +1,3 @@
<Context>
- <Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" configFile="conf/fediz_config.xml" />
- <!--<Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" issuerURL="https://localhost:9443/fedizidp/" truststoreFile="conf/stsstore.jks" truststorePassword="stsspass" trustedIssuer=".*CN=www.sts.com.*" />-->
- <!--Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" issuerCallbackHandler="org.apache.cxf.fediz.tomcat.DummyIDPCallbackHandler" truststoreFile="conf/stsstore.jks" truststorePassword="stsspass" />-->
+ <Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" configFile="conf/fediz_config.xml" />
</Context>
Modified: cxf/fediz/trunk/examples/wsclientWebapp/webapp/pom.xml
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/examples/wsclientWebapp/webapp/pom.xml?rev=1345768&r1=1345767&r2=1345768&view=diff
==============================================================================
--- cxf/fediz/trunk/examples/wsclientWebapp/webapp/pom.xml (original)
+++ cxf/fediz/trunk/examples/wsclientWebapp/webapp/pom.xml Sun Jun 3 20:25:34 2012
@@ -92,6 +92,10 @@
<artifactId>wss4j</artifactId>
<groupId>org.apache.ws.security</groupId>
</exclusion>
+ <exclusion>
+ <artifactId>ehcache-core</artifactId>
+ <groupId>net.sf.ehcache</groupId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
Modified: cxf/fediz/trunk/examples/wsclientWebapp/webapp/src/main/webapp/META-INF/context.xml
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/examples/wsclientWebapp/webapp/src/main/webapp/META-INF/context.xml?rev=1345768&r1=1345767&r2=1345768&view=diff
==============================================================================
--- cxf/fediz/trunk/examples/wsclientWebapp/webapp/src/main/webapp/META-INF/context.xml (original)
+++ cxf/fediz/trunk/examples/wsclientWebapp/webapp/src/main/webapp/META-INF/context.xml Sun Jun 3 20:25:34 2012
@@ -1,5 +1,3 @@
<Context>
- <Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" configFile="conf/fediz_config.xml" />
- <!--<Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" issuerURL="https://localhost:9443/fedizidp/" truststoreFile="conf/stsstore.jks" truststorePassword="stsspass" trustedIssuer=".*CN=www.sts.com.*" />-->
- <!--Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" issuerCallbackHandler="org.apache.cxf.fediz.tomcat.DummyIDPCallbackHandler" truststoreFile="conf/stsstore.jks" truststorePassword="stsspass" />-->
+ <Valve className="org.apache.cxf.fediz.tomcat.FederationAuthenticator" configFile="conf/fediz_config.xml" />
</Context>
Modified: cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SAMLTokenValidator.java
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SAMLTokenValidator.java?rev=1345768&r1=1345767&r2=1345768&view=diff
==============================================================================
--- cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SAMLTokenValidator.java (original)
+++ cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SAMLTokenValidator.java Sun Jun 3 20:25:34 2012
@@ -20,9 +20,7 @@
package org.apache.cxf.fediz.core.saml;
import java.io.File;
-import java.io.IOException;
import java.net.URI;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -32,25 +30,22 @@ import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
import org.w3c.dom.Element;
import org.apache.cxf.fediz.core.Claim;
import org.apache.cxf.fediz.core.ClaimCollection;
import org.apache.cxf.fediz.core.TokenValidator;
import org.apache.cxf.fediz.core.TokenValidatorResponse;
+import org.apache.cxf.fediz.core.config.CertificateValidationMethod;
import org.apache.cxf.fediz.core.config.FederationContext;
import org.apache.cxf.fediz.core.config.FederationProtocol;
import org.apache.cxf.fediz.core.config.KeyStore;
import org.apache.cxf.fediz.core.config.TrustManager;
import org.apache.cxf.fediz.core.config.TrustedIssuer;
+import org.apache.cxf.fediz.core.saml.SamlAssertionValidator.TRUST_TYPE;
import org.apache.ws.security.SAMLTokenPrincipal;
import org.apache.ws.security.WSDocInfo;
-import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.components.crypto.Crypto;
@@ -59,12 +54,9 @@ import org.apache.ws.security.handler.Re
import org.apache.ws.security.saml.SAMLKeyInfo;
import org.apache.ws.security.saml.ext.AssertionWrapper;
import org.apache.ws.security.validate.Credential;
-import org.apache.ws.security.validate.SignatureTrustValidator;
import org.joda.time.DateTime;
import org.opensaml.common.SAMLVersion;
import org.opensaml.xml.XMLObject;
-import org.opensaml.xml.validation.ValidationException;
-import org.opensaml.xml.validation.ValidatorSuite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -87,29 +79,8 @@ public class SAMLTokenValidator implemen
public TokenValidatorResponse validateAndProcessToken(Element token,
FederationContext config) {
- try {
- String trustStoreFile;
- String trustStorePw;
- //[TODO] Support more than one truststore
- TrustManager tm = config.getCertificateStores().get(0);
- KeyStore ks = tm.getKeyStore();
- if (ks.getFile() != null && !ks.getFile().isEmpty()) {
- trustStoreFile = ks.getFile();
- trustStorePw = ks.getPassword();
- } else {
- throw new IllegalStateException("No certificate store configured");
- }
-
- File f = new File(trustStoreFile);
- if (!f.exists() && config.getRelativePath() != null && !config.getRelativePath().isEmpty()) {
- trustStoreFile = config.getRelativePath().concat(File.separator + trustStoreFile);
- }
-
- Properties sigProperties = createCryptoProviderProperties(trustStoreFile, trustStorePw);
-
- Crypto sigCrypto = CryptoFactory.getInstance(sigProperties);
+ try {
RequestData requestData = new RequestData();
- requestData.setSigCrypto(sigCrypto);
WSSConfig wssConfig = WSSConfig.getNewInstance();
requestData.setWssConfig(wssConfig);
// not needed as no private key must be read
@@ -124,44 +95,61 @@ public class SAMLTokenValidator implemen
// Verify the signature
assertion.verifySignature(requestData,
new WSDocInfo(token.getOwnerDocument()));
-
- // Validate the assertion against schemas/profiles
- validateAssertion(assertion);
-
- // Validate Conditions
- if (config.isDetectExpiredTokens() && !validateConditions(assertion, config)) {
- throw new RuntimeException(
- "Error in validating conditions of the received Assertion"
- );
- }
// Now verify trust on the signature
Credential trustCredential = new Credential();
SAMLKeyInfo samlKeyInfo = assertion.getSignatureKeyInfo();
trustCredential.setPublicKey(samlKeyInfo.getPublicKey());
trustCredential.setCertificates(samlKeyInfo.getCerts());
+ trustCredential.setAssertion(assertion);
- SignatureTrustValidator trustValidator = new SignatureTrustValidator();
- trustValidator.validate(trustCredential, requestData);
-
+ SamlAssertionValidator trustValidator = new SamlAssertionValidator();
+ trustValidator.setFutureTTL(config.getMaximumClockSkew().intValue());
+
+ boolean trusted = false;
String assertionIssuer = assertion.getIssuerString();
-
- // Finally check that subject DN of the signing certificate matches
- // a known constraint
- X509Certificate cert = null;
- if (trustCredential.getCertificates() != null) {
- cert = trustCredential.getCertificates()[0];
- }
-
- // [TODO] Support more than one trusted issuer
+
List<TrustedIssuer> trustedIssuers = config.getTrustedIssuers();
- TrustedIssuer ti = trustedIssuers.get(0);
- List<String> subjectConstraints = Collections.singletonList(ti.getSubject());
-
- CertConstraintsParser certConstraints = new CertConstraintsParser();
- certConstraints.setSubjectConstraints(subjectConstraints);
-
- if (!certConstraints.matches(cert)) {
+ for (TrustedIssuer ti : trustedIssuers) {
+ List<String> subjectConstraints = Collections.singletonList(ti.getSubject());
+ if (ti.getCertificateValidationMethod().equals(CertificateValidationMethod.CHAIN_TRUST)) {
+ trustValidator.setSubjectConstraints(subjectConstraints);
+ trustValidator.setSignatureTrustType(TRUST_TYPE.CHAIN_TRUST_CONSTRAINTS);
+ } else if (ti.getCertificateValidationMethod().equals(CertificateValidationMethod.PEER_TRUST)) {
+ trustValidator.setSignatureTrustType(TRUST_TYPE.PEER_TRUST);
+ } else {
+ throw new IllegalStateException("Unsupported certificate validation method: "
+ + ti.getCertificateValidationMethod());
+ }
+ try {
+ for (TrustManager tm: config.getCertificateStores()) {
+ try {
+ Properties sigProperties = createCryptoProperties(config, tm);
+ Crypto sigCrypto = CryptoFactory.getInstance(sigProperties);
+ requestData.setSigCrypto(sigCrypto);
+ trustValidator.validate(trustCredential, requestData);
+ trusted = true;
+ break;
+ } catch (Exception ex) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Issuer '" + ti.getName() + "' not validated in keystore '"
+ + tm.getKeyStore().getFile() + "'");
+ }
+ }
+ }
+ if (trusted) {
+ break;
+ }
+
+ } catch (Exception ex) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Issuer '" + assertionIssuer + "' doesn't match trusted issuer '" + ti.getName()
+ + "': " + ex.getMessage());
+ }
+ }
+ }
+
+ if (!trusted) {
throw new RuntimeException("Issuer '" + assertionIssuer
+ "' not trusted");
}
@@ -291,7 +279,11 @@ public class SAMLTokenValidator implemen
LOG.debug("parsing attribute: " + attribute.getName());
}
Claim c = new Claim();
- c.setClaimType(URI.create(attribute.getName()));
+ if (attribute.getName().startsWith(c.getNamespace().toString())) {
+ c.setClaimType(URI.create(attribute.getName().substring(c.getNamespace().toString().length() + 1)));
+ } else {
+ c.setClaimType(URI.create(attribute.getName()));
+ }
c.setIssuer(assertion.getIssuer().getNameQualifier());
List<String> valueList = new ArrayList<String>();
@@ -322,10 +314,12 @@ public class SAMLTokenValidator implemen
List<String> values = new ArrayList<String>();
values.add((String)oValue); //add existing value
values.addAll(valueList);
+ t.setValue(values);
} else if (oValue instanceof List<?>) {
//more than one child element AttributeValue
List<String> values = (List<String>)oValue;
values.addAll(valueList);
+ t.setValue(values);
} else {
throw new IllegalStateException("Invalid value type of Claim value");
}
@@ -376,81 +370,38 @@ public class SAMLTokenValidator implemen
}
- protected Properties createCryptoProviderProperties(String truststoreFile,
- String truststorePassword) {
+ private Properties createCryptoProperties(FederationContext config, TrustManager tm) {
+ String trustStoreFile = null;
+ String trustStorePw = null;
+ KeyStore ks = tm.getKeyStore();
+ if (ks.getFile() != null && !ks.getFile().isEmpty()) {
+ trustStoreFile = ks.getFile();
+ trustStorePw = ks.getPassword();
+ } else {
+ throw new IllegalStateException("No certificate store configured");
+ }
+ File f = new File(trustStoreFile);
+ if (!f.exists() && config.getRelativePath() != null && !config.getRelativePath().isEmpty()) {
+ trustStoreFile = config.getRelativePath().concat(File.separator + trustStoreFile);
+ }
+
+ if (trustStoreFile == null || trustStoreFile.isEmpty()) {
+ throw new NullPointerException("truststoreFile not configured");
+ }
+ if (trustStorePw == null || trustStorePw.isEmpty()) {
+ throw new NullPointerException("trustStorePw not configured");
+ }
Properties p = new Properties();
p.put("org.apache.ws.security.crypto.provider",
"org.apache.ws.security.components.crypto.Merlin");
p.put("org.apache.ws.security.crypto.merlin.keystore.type", "jks");
p.put("org.apache.ws.security.crypto.merlin.keystore.password",
- truststorePassword);
+ trustStorePw);
p.put("org.apache.ws.security.crypto.merlin.keystore.file",
- truststoreFile);
+ trustStoreFile);
return p;
}
- /**
- * Validate the assertion against schemas/profiles
- */
- protected void validateAssertion(AssertionWrapper assertion) throws WSSecurityException {
- if (assertion.getSaml1() != null) {
- ValidatorSuite schemaValidators =
- org.opensaml.Configuration.getValidatorSuite("saml1-schema-validator");
- ValidatorSuite specValidators =
- org.opensaml.Configuration.getValidatorSuite("saml1-spec-validator");
- try {
- schemaValidators.validate(assertion.getSaml1());
- specValidators.validate(assertion.getSaml1());
- } catch (ValidationException e) {
- LOG.debug("Saml Validation error: " + e.getMessage());
- throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
- }
- } else if (assertion.getSaml2() != null) {
- ValidatorSuite schemaValidators =
- org.opensaml.Configuration.getValidatorSuite("saml2-core-schema-validator");
- ValidatorSuite specValidators =
- org.opensaml.Configuration.getValidatorSuite("saml2-core-spec-validator");
- try {
- schemaValidators.validate(assertion.getSaml2());
- specValidators.validate(assertion.getSaml2());
- } catch (ValidationException e) {
- LOG.debug("Saml Validation error: " + e.getMessage());
- throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
- }
- }
- }
-
- protected boolean validateConditions(
- AssertionWrapper assertion,
- FederationContext config
- ) {
- DateTime validFrom = null;
- DateTime validTill = null;
- if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)) {
- validFrom = assertion.getSaml2().getConditions().getNotBefore();
- validTill = assertion.getSaml2().getConditions().getNotOnOrAfter();
- } else {
- validFrom = assertion.getSaml1().getConditions().getNotBefore();
- validTill = assertion.getSaml1().getConditions().getNotOnOrAfter();
- }
-
- if (validFrom != null) {
- DateTime currentTime = new DateTime();
- currentTime = currentTime.plusSeconds(config.getMaximumClockSkew().intValue());
- if (validFrom.isAfter(currentTime)) {
- LOG.warn("SAML Token condition (Not Before) not met");
- return false;
- }
- }
-
- if (validTill != null && validTill.isBeforeNow()) {
- LOG.debug("SAML Token condition (Not On Or After) not met");
- return false;
- }
-
- return true;
- }
-
private Date getExpires(AssertionWrapper assertion) {
DateTime validTill = null;
if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)) {
@@ -465,25 +416,4 @@ public class SAMLTokenValidator implemen
return validTill.toDate();
}
- // A sample MyHandler class
- class PasswordCallbackHandler implements CallbackHandler {
- private String password;
-
- public PasswordCallbackHandler(String password) {
- this.password = password;
- }
-
- public void handle(Callback[] callbacks) throws IOException,
- UnsupportedCallbackException {
- for (int i = 0; i < callbacks.length; i++) {
- if (callbacks[i] instanceof WSPasswordCallback) {
- WSPasswordCallback nc = (WSPasswordCallback) callbacks[i];
- nc.setPassword(this.password);
- } else {
- throw new UnsupportedCallbackException(callbacks[i],
- "Unrecognized Callback");
- }
- }
- }
- }
}
Added: cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SamlAssertionValidator.java
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SamlAssertionValidator.java?rev=1345768&view=auto
==============================================================================
--- cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SamlAssertionValidator.java (added)
+++ cxf/fediz/trunk/plugins/core/src/main/java/org/apache/cxf/fediz/core/saml/SamlAssertionValidator.java Sun Jun 3 20:25:34 2012
@@ -0,0 +1,573 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core.saml;
+
+
+import java.math.BigInteger;
+import java.security.PublicKey;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoType;
+import org.apache.ws.security.handler.RequestData;
+import org.apache.ws.security.saml.SAMLKeyInfo;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.saml.ext.OpenSAMLUtil;
+import org.apache.ws.security.validate.Credential;
+import org.apache.ws.security.validate.Validator;
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLVersion;
+import org.opensaml.xml.validation.ValidationException;
+import org.opensaml.xml.validation.ValidatorSuite;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class validates a SAML Assertion, which is wrapped in an "AssertionWrapper" instance.
+ * It assumes that the AssertionWrapper instance has already verified the signature on the
+ * assertion (done by the SAMLTokenProcessor). It verifies trust in the signature, and also
+ * checks that the Subject contains a KeyInfo (and processes it) for the holder-of-key case,
+ * and verifies that the Assertion is signed as well for holder-of-key.
+ */
+public class SamlAssertionValidator implements Validator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SamlAssertionValidator.class);
+
+ public enum TRUST_TYPE { CHAIN_TRUST, CHAIN_TRUST_CONSTRAINTS, PEER_TRUST }
+
+ /**
+ * The time in seconds in the future within which the NotBefore time of an incoming
+ * Assertion is valid. The default is 60 seconds.
+ */
+ private int futureTTL = 60;
+
+ /**
+ * Defines the kind of trust which is required thus assertion signature validation is successful.
+ */
+ private TRUST_TYPE signatureTrustType = TRUST_TYPE.CHAIN_TRUST;
+
+ /**
+ * a collection of compiled regular expression patterns for the subject DN
+ */
+ private Collection<Pattern> subjectDNPatterns = new ArrayList<Pattern>();
+
+
+ /**
+ * Set the time in seconds in the future within which the NotBefore time of an incoming
+ * Assertion is valid. The default is 60 seconds.
+ */
+ public void setFutureTTL(int newFutureTTL) {
+ futureTTL = newFutureTTL;
+ }
+
+ /**
+ * Set the kind of trust. The default is CHAIN_TRUST.
+ */
+ public void setSignatureTrustType(TRUST_TYPE trustType) {
+ this.signatureTrustType = trustType;
+ }
+
+ /**
+ * Set a list of Strings corresponding to regular expression constraints on
+ * the subject DN of a certificate
+ */
+ public void setSubjectConstraints(List<String> constraints) {
+ if (constraints != null) {
+ subjectDNPatterns = new ArrayList<Pattern>();
+ for (String constraint : constraints) {
+ try {
+ subjectDNPatterns.add(Pattern.compile(constraint.trim()));
+ } catch (PatternSyntaxException ex) {
+ // LOG.severe(ex.getMessage());
+ throw ex;
+ }
+ }
+ }
+ }
+
+ /**
+ * Validate the credential argument. It must contain a non-null AssertionWrapper.
+ * A Crypto and a CallbackHandler implementation is also required to be set.
+ *
+ * @param credential the Credential to be validated
+ * @param data the RequestData associated with the request
+ * @throws WSSecurityException on a failed validation
+ */
+ public Credential validate(Credential credential, RequestData data) throws WSSecurityException {
+ if (credential == null || credential.getAssertion() == null) {
+ throw new WSSecurityException(WSSecurityException.FAILURE, "noCredential");
+ }
+ AssertionWrapper assertion = credential.getAssertion();
+
+ // Check HOK requirements
+ String confirmMethod = null;
+ List<String> methods = assertion.getConfirmationMethods();
+ if (methods != null && methods.size() > 0) {
+ confirmMethod = methods.get(0);
+ }
+ if (OpenSAMLUtil.isMethodHolderOfKey(confirmMethod)) {
+ if (assertion.getSubjectKeyInfo() == null) {
+ LOG.debug("There is no Subject KeyInfo to match the holder-of-key subject conf method");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "noKeyInSAMLToken");
+ }
+ // The assertion must have been signed for HOK
+ if (!assertion.isSigned()) {
+ LOG.debug("A holder-of-key assertion must be signed");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ // Check conditions
+ //[TODO] Commented out due to testcase issue
+ //checkConditions(assertion);
+
+ // Validate the assertion against schemas/profiles
+ validateAssertion(assertion);
+
+ // Verify trust on the signature
+ if (assertion.isSigned()) {
+ verifySignedAssertion(assertion, data);
+ }
+ return credential;
+ }
+
+ /**
+ * Verify trust in the signature of a signed Assertion. This method is separate so that
+ * the user can override if if they want.
+ * @param assertion The signed Assertion
+ * @param data The RequestData context
+ * @return A Credential instance
+ * @throws WSSecurityException
+ */
+ protected Credential verifySignedAssertion(
+ AssertionWrapper assertion,
+ RequestData data
+ ) throws WSSecurityException {
+ Credential credential = new Credential();
+ SAMLKeyInfo samlKeyInfo = assertion.getSignatureKeyInfo();
+ credential.setPublicKey(samlKeyInfo.getPublicKey());
+ credential.setCertificates(samlKeyInfo.getCerts());
+
+ if (credential == null) {
+ throw new WSSecurityException(WSSecurityException.FAILURE, "noCredential");
+ }
+ X509Certificate[] certs = credential.getCertificates();
+ PublicKey publicKey = credential.getPublicKey();
+ Crypto crypto = getCrypto(data);
+ if (crypto == null) {
+ throw new WSSecurityException(WSSecurityException.FAILURE, "noSigCryptoFile");
+ }
+
+ if (certs != null && certs.length > 0) {
+ validateCertificates(certs);
+ boolean trust = false;
+ boolean enableRevocation = data.isRevocationEnabled();
+ if (certs.length == 1) {
+ trust = verifyTrustInCert(certs[0], crypto, enableRevocation);
+ } else {
+ trust = verifyTrustInCerts(certs, crypto, enableRevocation);
+ }
+ if (trust) {
+ if (signatureTrustType.equals(TRUST_TYPE.CHAIN_TRUST_CONSTRAINTS)) {
+ if (matches(certs[0])) {
+ return credential;
+ } else {
+ throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
+ }
+ } else {
+ return credential;
+ }
+ }
+ }
+ if (publicKey != null) {
+ boolean trust = validatePublicKey(publicKey, crypto);
+ if (trust) {
+ return credential;
+ }
+ }
+ throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
+ }
+
+ protected Crypto getCrypto(RequestData data) {
+ return data.getSigCrypto();
+ }
+
+
+ /**
+ * Validate the certificates by checking the validity of each cert
+ * @throws WSSecurityException
+ */
+ protected void validateCertificates(X509Certificate[] certificates)
+ throws WSSecurityException {
+ try {
+ for (int i = 0; i < certificates.length; i++) {
+ certificates[i].checkValidity();
+ }
+ } catch (CertificateExpiredException e) {
+ throw new WSSecurityException(
+ WSSecurityException.FAILED_CHECK, "invalidCert", null, e
+ );
+ } catch (CertificateNotYetValidException e) {
+ throw new WSSecurityException(
+ WSSecurityException.FAILED_CHECK, "invalidCert", null, e
+ );
+ }
+ }
+
+ /**
+ * Evaluate whether a given certificate should be trusted.
+ *
+ * Policy used in this implementation:
+ * 1. Search the keystore for the transmitted certificate
+ * 2. Search the keystore for a connection to the transmitted certificate
+ * (that is, search for certificate(s) of the issuer of the transmitted certificate
+ * 3. Verify the trust path for those certificates found because the search for the issuer
+ * might be fooled by a phony DN (String!)
+ *
+ * @param cert the certificate that should be validated against the keystore
+ * @param crypto A crypto instance to use for trust validation
+ * @return true if the certificate is trusted, false if not
+ * @throws WSSecurityException
+ */
+ @Deprecated
+ protected boolean verifyTrustInCert(X509Certificate cert, Crypto crypto)
+ throws WSSecurityException {
+ return verifyTrustInCert(cert, crypto, false);
+ }
+
+ /**
+ * Evaluate whether a given certificate should be trusted.
+ *
+ * Policy used in this implementation:
+ * 1. Search the keystore for the transmitted certificate
+ * 2. Search the keystore for a connection to the transmitted certificate
+ * (that is, search for certificate(s) of the issuer of the transmitted certificate
+ * 3. Verify the trust path for those certificates found because the search for the issuer
+ * might be fooled by a phony DN (String!)
+ *
+ * @param cert the certificate that should be validated against the keystore
+ * @param crypto A crypto instance to use for trust validation
+ * @param enableRevocation Whether revocation is enabled or not
+ * @return true if the certificate is trusted, false if not
+ * @throws WSSecurityException
+ */
+ protected boolean verifyTrustInCert(
+ X509Certificate cert,
+ Crypto crypto,
+ boolean enableRevocation
+ ) throws WSSecurityException {
+ String subjectString = cert.getSubjectX500Principal().getName();
+ String issuerString = cert.getIssuerX500Principal().getName();
+ BigInteger issuerSerial = cert.getSerialNumber();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Transmitted certificate has subject " + subjectString);
+ LOG.debug(
+ "Transmitted certificate has issuer " + issuerString + " (serial "
+ + issuerSerial + ")"
+ );
+ }
+
+ //
+ // FIRST step - Search the keystore for the transmitted certificate
+ // If peer trust is enforced then validation fails if
+ // certificate not found in keystore
+ //
+ boolean isInKeystore = isCertificateInKeyStore(crypto, cert);
+ if (!enableRevocation && isInKeystore) {
+ return true;
+ }
+ if (!isInKeystore && signatureTrustType.equals(TRUST_TYPE.PEER_TRUST)) {
+ return false;
+ }
+
+ //
+ // SECOND step - Search for the issuer cert (chain) of the transmitted certificate in the
+ // keystore or the truststore
+ //
+ CryptoType cryptoType = new CryptoType(CryptoType.TYPE.SUBJECT_DN);
+ cryptoType.setSubjectDN(issuerString);
+ X509Certificate[] foundCerts = crypto.getX509Certificates(cryptoType);
+
+ // If the certs have not been found, the issuer is not in the keystore/truststore
+ // As a direct result, do not trust the transmitted certificate
+ if (foundCerts == null || foundCerts.length < 1) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "No certs found in keystore for issuer " + issuerString
+ + " of certificate for " + subjectString
+ );
+ }
+ return false;
+ }
+
+ //
+ // THIRD step
+ // Check the certificate trust path for the issuer cert chain
+ //
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Preparing to validate certificate path for issuer " + issuerString
+ );
+ }
+ //
+ // Form a certificate chain from the transmitted certificate
+ // and the certificate(s) of the issuer from the keystore/truststore
+ //
+ X509Certificate[] x509certs = new X509Certificate[foundCerts.length + 1];
+ x509certs[0] = cert;
+ for (int j = 0; j < foundCerts.length; j++) {
+ x509certs[j + 1] = (X509Certificate)foundCerts[j];
+ }
+
+ //
+ // Use the validation method from the crypto to check whether the subjects'
+ // certificate was really signed by the issuer stated in the certificate
+ //
+ if (crypto.verifyTrust(x509certs, enableRevocation)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Certificate path has been verified for certificate with subject "
+ + subjectString
+ );
+ }
+ return true;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Certificate path could not be verified for certificate with subject "
+ + subjectString
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Check to see if the certificate argument is in the keystore
+ * @param crypto A Crypto instance to use for trust validation
+ * @param cert The certificate to check
+ * @return true if cert is in the keystore
+ * @throws WSSecurityException
+ */
+ protected boolean isCertificateInKeyStore(
+ Crypto crypto,
+ X509Certificate cert
+ ) throws WSSecurityException {
+ String issuerString = cert.getIssuerX500Principal().getName();
+ BigInteger issuerSerial = cert.getSerialNumber();
+
+ CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
+ cryptoType.setIssuerSerial(issuerString, issuerSerial);
+ X509Certificate[] foundCerts = crypto.getX509Certificates(cryptoType);
+
+ //
+ // If a certificate has been found, the certificates must be compared
+ // to ensure against phony DNs (compare encoded form including signature)
+ //
+ if (foundCerts != null && foundCerts[0] != null && foundCerts[0].equals(cert)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Direct trust for certificate with " + cert.getSubjectX500Principal().getName()
+ );
+ }
+ return true;
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "No certificate found for subject from issuer with " + issuerString
+ + " (serial " + issuerSerial + ")"
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Evaluate whether the given certificate chain should be trusted.
+ *
+ * @param certificates the certificate chain that should be validated against the keystore
+ * @param crypto A Crypto instance to use for trust validation
+ * @return true if the certificate chain is trusted, false if not
+ * @throws WSSecurityException
+ */
+ @Deprecated
+ protected boolean verifyTrustInCerts(
+ X509Certificate[] certificates,
+ Crypto crypto
+ ) throws WSSecurityException {
+ return verifyTrustInCerts(certificates, crypto, false);
+ }
+
+ /**
+ * Evaluate whether the given certificate chain should be trusted.
+ *
+ * @param certificates the certificate chain that should be validated against the keystore
+ * @param crypto A Crypto instance
+ * @param enableRevocation Whether revocation is enabled or not
+ * @return true if the certificate chain is trusted, false if not
+ * @throws WSSecurityException
+ */
+ protected boolean verifyTrustInCerts(
+ X509Certificate[] certificates,
+ Crypto crypto,
+ boolean enableRevocation
+ ) throws WSSecurityException {
+ if (certificates == null || certificates.length < 2) {
+ return false;
+ }
+
+ String subjectString = certificates[0].getSubjectX500Principal().getName();
+ //
+ // Use the validation method from the crypto to check whether the subjects'
+ // certificate was really signed by the issuer stated in the certificate
+ //
+ if (crypto.verifyTrust(certificates, enableRevocation)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Certificate path has been verified for certificate with subject "
+ + subjectString
+ );
+ }
+ return true;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Certificate path could not be verified for certificate with subject "
+ + subjectString
+ );
+ }
+
+ return false;
+ }
+
+ /**
+ * Validate a public key
+ * @throws WSSecurityException
+ */
+ protected boolean validatePublicKey(PublicKey publicKey, Crypto crypto)
+ throws WSSecurityException {
+ return crypto.verifyTrust(publicKey);
+ }
+
+ /**
+ * Check the Conditions of the Assertion.
+ */
+ protected void checkConditions(AssertionWrapper assertion) throws WSSecurityException {
+ DateTime validFrom = null;
+ DateTime validTill = null;
+ if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)
+ && assertion.getSaml2().getConditions() != null) {
+ validFrom = assertion.getSaml2().getConditions().getNotBefore();
+ validTill = assertion.getSaml2().getConditions().getNotOnOrAfter();
+ } else if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_11)
+ && assertion.getSaml1().getConditions() != null) {
+ validFrom = assertion.getSaml1().getConditions().getNotBefore();
+ validTill = assertion.getSaml1().getConditions().getNotOnOrAfter();
+ }
+
+ if (validFrom != null) {
+ DateTime currentTime = new DateTime();
+ currentTime = currentTime.plusSeconds(futureTTL);
+ if (validFrom.isAfter(currentTime)) {
+ LOG.debug("SAML Token condition (Not Before) not met");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ if (validTill != null && validTill.isBeforeNow()) {
+ LOG.debug("SAML Token condition (Not On Or After) not met");
+ throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
+ }
+ }
+
+ /**
+ * Validate the assertion against schemas/profiles
+ */
+ protected void validateAssertion(AssertionWrapper assertion) throws WSSecurityException {
+ if (assertion.getSaml1() != null) {
+ ValidatorSuite schemaValidators =
+ org.opensaml.Configuration.getValidatorSuite("saml1-schema-validator");
+ ValidatorSuite specValidators =
+ org.opensaml.Configuration.getValidatorSuite("saml1-spec-validator");
+ try {
+ schemaValidators.validate(assertion.getSaml1());
+ specValidators.validate(assertion.getSaml1());
+ } catch (ValidationException e) {
+ LOG.debug("Saml Validation error: " + e.getMessage(), e);
+ throw new WSSecurityException(
+ WSSecurityException.FAILURE, "invalidSAMLsecurity", null, e
+ );
+ }
+ } else if (assertion.getSaml2() != null) {
+ ValidatorSuite schemaValidators =
+ org.opensaml.Configuration.getValidatorSuite("saml2-core-schema-validator");
+ ValidatorSuite specValidators =
+ org.opensaml.Configuration.getValidatorSuite("saml2-core-spec-validator");
+ try {
+ schemaValidators.validate(assertion.getSaml2());
+ specValidators.validate(assertion.getSaml2());
+ } catch (ValidationException e) {
+ LOG.debug("Saml Validation error: " + e.getMessage(), e);
+ throw new WSSecurityException(
+ WSSecurityException.FAILURE, "invalidSAMLsecurity", null, e
+ );
+ }
+ }
+ }
+
+ /**
+ * @return true if the certificate's SubjectDN matches the constraints
+ * defined in the subject DNConstraints; false, otherwise. The
+ * certificate subject DN only has to match ONE of the subject cert
+ * constraints (not all).
+ */
+ public boolean matches(final java.security.cert.X509Certificate cert) {
+ if (!subjectDNPatterns.isEmpty()) {
+ if (cert == null) {
+ return false;
+ }
+ String subjectName = cert.getSubjectX500Principal().getName();
+ boolean subjectMatch = false;
+ for (Pattern subjectDNPattern : subjectDNPatterns) {
+ final Matcher matcher = subjectDNPattern.matcher(subjectName);
+ if (matcher.matches()) {
+ subjectMatch = true;
+ break;
+ }
+ }
+ if (!subjectMatch) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+}
Added: cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/common/STSUtil.java
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/common/STSUtil.java?rev=1345768&view=auto
==============================================================================
--- cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/common/STSUtil.java (added)
+++ cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/common/STSUtil.java Sun Jun 3 20:25:34 2012
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.common;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+public class STSUtil {
+
+ public static final String SAMPLE_RSTR_MSG =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<SOAP-ENV:Envelope "
+ + "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
+ + "<SOAP-ENV:Body>"
+ + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
+ + "<value xmlns=\"\">15</value>"
+ + "</add>"
+ + "</SOAP-ENV:Body>"
+ + "</SOAP-ENV:Envelope>";
+
+
+ public static final String SAMPLE_RSTR_COLL_MSG =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<RequestSecurityTokenResponseCollection "
+ + "xmlns=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\"> "
+ + "<RequestSecurityTokenResponse>"
+ + "<RequestedSecurityToken>"
+ + "</RequestedSecurityToken>"
+ + "</RequestSecurityTokenResponse>"
+ + "</RequestSecurityTokenResponseCollection>";
+
+
+ private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+ static {
+ factory.setNamespaceAware(true);
+ }
+
+ protected STSUtil() {
+
+ }
+
+ /**
+ * Convert an XML document as a String to a org.w3c.dom.Document.
+ */
+ public static org.w3c.dom.Document toSOAPPart(String xml) throws Exception {
+ InputStream in = new ByteArrayInputStream(xml.getBytes());
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ return builder.parse(in);
+ }
+
+}
Added: cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java?rev=1345768&view=auto
==============================================================================
--- cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java (added)
+++ cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/AbstractSAMLCallbackHandler.java Sun Jun 3 20:25:34 2012
@@ -0,0 +1,282 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core;
+
+
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.message.WSSecEncryptedKey;
+import org.apache.ws.security.saml.ext.SAMLCallback;
+import org.apache.ws.security.saml.ext.bean.ActionBean;
+import org.apache.ws.security.saml.ext.bean.AttributeBean;
+import org.apache.ws.security.saml.ext.bean.AttributeStatementBean;
+import org.apache.ws.security.saml.ext.bean.AuthDecisionStatementBean;
+import org.apache.ws.security.saml.ext.bean.AuthenticationStatementBean;
+import org.apache.ws.security.saml.ext.bean.ConditionsBean;
+import org.apache.ws.security.saml.ext.bean.KeyInfoBean;
+import org.apache.ws.security.saml.ext.bean.KeyInfoBean.CERT_IDENTIFIER;
+import org.apache.ws.security.saml.ext.bean.SubjectBean;
+import org.apache.ws.security.saml.ext.bean.SubjectConfirmationDataBean;
+import org.apache.ws.security.saml.ext.bean.SubjectLocalityBean;
+
+
+
+/**
+ * A base implementation of a Callback Handler for a SAML assertion. By default it creates an
+ * authentication assertion.
+ */
+public abstract class AbstractSAMLCallbackHandler implements CallbackHandler {
+
+ public enum Statement {
+ AUTHN, ATTR, AUTHZ
+ };
+
+ public enum MultiValue {
+ MULTI_VALUE, MULTI_ATTR, ENC_VALUE
+ };
+
+ protected String subjectName;
+ protected String subjectQualifier;
+ protected String confirmationMethod;
+ protected X509Certificate[] certs;
+ protected Statement statement = Statement.AUTHN;
+ protected CERT_IDENTIFIER certIdentifier = CERT_IDENTIFIER.X509_CERT;
+ protected byte[] ephemeralKey;
+ protected String issuer;
+ protected String subjectNameIDFormat;
+ protected String subjectLocalityIpAddress;
+ protected String subjectLocalityDnsAddress;
+ protected String resource;
+ protected List<?> customAttributeValues;
+ protected ConditionsBean conditions;
+ protected SubjectConfirmationDataBean subjectConfirmationData;
+ protected List<String> roles = Arrays.asList("User", "Admin");
+ protected Map<String, String> claims;
+ protected MultiValue multiValueType = MultiValue.MULTI_VALUE;
+ protected String roleSeperator = ",";
+
+ public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData) {
+ this.subjectConfirmationData = subjectConfirmationData;
+ }
+
+ public void setConditions(ConditionsBean conditionsBean) {
+ this.conditions = conditionsBean;
+ }
+
+ public void setConfirmationMethod(String confMethod) {
+ confirmationMethod = confMethod;
+ }
+
+ public void setStatement(Statement statement) {
+ this.statement = statement;
+ }
+
+ public void setCertIdentifier(CERT_IDENTIFIER certIdentifier) {
+ this.certIdentifier = certIdentifier;
+ }
+
+ public void setCerts(X509Certificate[] certs) {
+ this.certs = certs;
+ }
+
+ public byte[] getEphemeralKey() {
+ return ephemeralKey;
+ }
+
+ public void setIssuer(String issuer) {
+ this.issuer = issuer;
+ }
+
+ public void setSubjectNameIDFormat(String subjectNameIDFormat) {
+ this.subjectNameIDFormat = subjectNameIDFormat;
+ }
+
+ public void setSubjectName(String subjectName) {
+ this.subjectName = subjectName;
+ }
+
+ public void setSubjectLocality(String ipAddress, String dnsAddress) {
+ this.subjectLocalityIpAddress = ipAddress;
+ this.subjectLocalityDnsAddress = dnsAddress;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public void setCustomAttributeValues(List<?> customAttributeValues) {
+ this.customAttributeValues = customAttributeValues;
+ }
+
+ public void setRoles(List<String> roles) {
+ this.roles = roles;
+ }
+
+ public void setClaims(Map<String, String> claims) {
+ this.claims = claims;
+ }
+
+ public void setMultiValueType(MultiValue multiValueType) {
+ this.multiValueType = multiValueType;
+ }
+
+ /**
+ * Note that the SubjectBean parameter should be null for SAML2.0
+ */
+ protected void createAndSetStatement(SubjectBean subjectBean, SAMLCallback callback) {
+ if (statement == Statement.AUTHN) {
+ AuthenticationStatementBean authBean = new AuthenticationStatementBean();
+ if (subjectBean != null) {
+ authBean.setSubject(subjectBean);
+ }
+ if (subjectLocalityIpAddress != null || subjectLocalityDnsAddress != null) {
+ SubjectLocalityBean subjectLocality = new SubjectLocalityBean();
+ subjectLocality.setIpAddress(subjectLocalityIpAddress);
+ subjectLocality.setDnsAddress(subjectLocalityDnsAddress);
+ authBean.setSubjectLocality(subjectLocality);
+ }
+ authBean.setAuthenticationMethod("Password");
+ callback.setAuthenticationStatementData(Collections.singletonList(authBean));
+ } else if (statement == Statement.ATTR) {
+ AttributeStatementBean attrStateBean = new AttributeStatementBean();
+
+ if (this.multiValueType.equals(MultiValue.MULTI_VALUE)
+ || this.multiValueType.equals(MultiValue.ENC_VALUE)) {
+// <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
+// AttributeNamespace="http://schemas.xmlsoap.org/claims" AttributeName="roles">
+// <saml:AttributeValue>Value1</saml:AttributeValue>
+// <saml:AttributeValue>Value2</saml:AttributeValue>
+// </saml:Attribute>
+// or
+// <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
+// AttributeNamespace="http://schemas.xmlsoap.org/claims" AttributeName="roles">
+// <saml:AttributeValue>Value1,Value2</saml:AttributeValue>
+// </saml:Attribute>
+ AttributeBean attributeBean = new AttributeBean();
+ if (subjectBean != null) {
+ attrStateBean.setSubject(subjectBean);
+ attributeBean.setSimpleName("role");
+ attributeBean.setQualifiedName(FederationConstants.DEFAULT_ROLE_URI.toString());
+ } else {
+ attributeBean.setQualifiedName(FederationConstants.DEFAULT_ROLE_URI.toString());
+ }
+ if (this.multiValueType.equals(MultiValue.MULTI_VALUE)) {
+ attributeBean.setAttributeValues(roles);
+ } else {
+ StringBuffer sb = new StringBuffer();
+ for (String role: roles) {
+ sb.append(role).append(this.roleSeperator);
+ }
+ String value = sb.substring(0, sb.length() - this.roleSeperator.length());
+ attributeBean.setAttributeValues(Collections.singletonList(value));
+ }
+ attrStateBean.setSamlAttributes(Collections.singletonList(attributeBean));
+ } else if (this.multiValueType.equals(MultiValue.MULTI_ATTR)) {
+// <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
+// AttributeNamespace="http://schemas.xmlsoap.org/claims" AttributeName="roles">
+// <saml:AttributeValue>Value1</saml:AttributeValue>
+// </saml:Attribute>
+// <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
+// AttributeNamespace="http://schemas.xmlsoap.org/claims" AttributeName="roles">
+// <saml:AttributeValue>Value2</saml:AttributeValue>
+// </saml:Attribute>
+
+ List<AttributeBean> attrBeans = new ArrayList<AttributeBean>();
+ for (String role: roles) {
+ AttributeBean attributeBean = new AttributeBean();
+ if (subjectBean != null) {
+ attrStateBean.setSubject(subjectBean);
+ attributeBean.setSimpleName("role");
+ attributeBean.setQualifiedName(FederationConstants.DEFAULT_ROLE_URI.toString());
+ } else {
+ attributeBean.setQualifiedName(FederationConstants.DEFAULT_ROLE_URI.toString());
+ }
+ attributeBean.setAttributeValues(Collections.singletonList(role));
+ attrBeans.add(attributeBean);
+ }
+ attrStateBean.setSamlAttributes(attrBeans);
+ }
+
+ callback.setAttributeStatementData(Collections.singletonList(attrStateBean));
+
+ } else {
+ AuthDecisionStatementBean authzBean = new AuthDecisionStatementBean();
+ if (subjectBean != null) {
+ authzBean.setSubject(subjectBean);
+ }
+ ActionBean actionBean = new ActionBean();
+ actionBean.setContents("Read");
+ authzBean.setActions(Collections.singletonList(actionBean));
+ authzBean.setResource("endpoint");
+ authzBean.setDecision(AuthDecisionStatementBean.Decision.PERMIT);
+ authzBean.setResource(resource);
+ callback.setAuthDecisionStatementData(Collections.singletonList(authzBean));
+ }
+ }
+
+ protected KeyInfoBean createKeyInfo() throws Exception {
+ KeyInfoBean keyInfo = new KeyInfoBean();
+ if (statement == Statement.AUTHN) {
+ keyInfo.setCertificate(certs[0]);
+ keyInfo.setCertIdentifer(certIdentifier);
+ } else if (statement == Statement.ATTR) {
+ // Build a new Document
+ DocumentBuilderFactory docBuilderFactory =
+ DocumentBuilderFactory.newInstance();
+ docBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+
+ // Create an Encrypted Key
+ WSSecEncryptedKey encrKey = new WSSecEncryptedKey();
+ encrKey.setKeyIdentifierType(WSConstants.ISSUER_SERIAL);
+ encrKey.setUseThisCert(certs[0]);
+ encrKey.prepare(doc, null);
+ ephemeralKey = encrKey.getEphemeralKey();
+ Element encryptedKeyElement = encrKey.getEncryptedKeyElement();
+
+ // Append the EncryptedKey to a KeyInfo element
+ Element keyInfoElement =
+ doc.createElementNS(
+ WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":" + WSConstants.KEYINFO_LN
+ );
+ keyInfoElement.setAttributeNS(
+ WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX, WSConstants.SIG_NS
+ );
+ keyInfoElement.appendChild(encryptedKeyElement);
+
+ keyInfo.setElement(keyInfoElement);
+ }
+ return keyInfo;
+ }
+}
Modified: cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/FederationProcessorTest.java
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/FederationProcessorTest.java?rev=1345768&r1=1345767&r2=1345768&view=diff
==============================================================================
--- cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/FederationProcessorTest.java (original)
+++ cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/FederationProcessorTest.java Sun Jun 3 20:25:34 2012
@@ -19,44 +19,62 @@
package org.apache.cxf.fediz.core;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
import java.net.URL;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
import junit.framework.Assert;
+import org.apache.cxf.fediz.common.STSUtil;
import org.apache.cxf.fediz.common.SecurityTestUtil;
+import org.apache.cxf.fediz.core.AbstractSAMLCallbackHandler.MultiValue;
import org.apache.cxf.fediz.core.config.FederationConfigurator;
import org.apache.cxf.fediz.core.config.FederationContext;
+import org.apache.cxf.fediz.core.config.FederationProtocol;
+import org.apache.ws.security.WSPasswordCallback;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.apache.ws.security.saml.ext.SAMLParms;
+import org.apache.ws.security.saml.ext.bean.ConditionsBean;
+import org.apache.ws.security.saml.ext.builder.SAML2Constants;
+import org.apache.ws.security.util.DOM2Writer;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+
import static org.junit.Assert.fail;
public class FederationProcessorTest {
private static final String TEST_USER = "alice";
- private static final String TEST_RSTR_ISSUER = "DoubleItSTSIssuer";
-
+ private static final String TEST_RSTR_ISSUER = "FedizSTSIssuer";
+ private static final String TEST_AUDIENCE = "https://localhost/fedizhelloworld";
+
private static final String CONFIG_FILE = "fediz_test_config.xml";
- private static final String CONFIG_FILE_WRONG_ISSUER = "fediz_test_config2.xml";
-
- private static String sRSTR;
- private static String sRSTRREPLAY;
-
+
+ private static Crypto crypto;
+ private static CallbackHandler cbPasswordHandler;
+ private static FederationConfigurator configurator;
+
+
@BeforeClass
- public static void readWResult() {
+ public static void init() {
try {
- sRSTR = loadResource("RSTR.xml");
- sRSTRREPLAY = loadResource("RSTR_replay.xml");
+ crypto = CryptoFactory.getInstance("signature.properties");
+ cbPasswordHandler = new KeystoreCallbackHandler();
+ getFederationConfigurator();
} catch (Exception e) {
e.printStackTrace();
}
- Assert.assertNotNull("RSTR resource null", sRSTR);
- Assert.assertNotNull(loadRootConfig());
+ Assert.assertNotNull(configurator);
}
@@ -65,141 +83,378 @@ public class FederationProcessorTest {
SecurityTestUtil.cleanup();
}
- private static String loadResource(String filename) throws IOException {
- InputStream is = null;
- try {
- is = FederationProcessorTest.class.getResourceAsStream("/" + filename);
- if (is == null) {
- throw new FileNotFoundException("Failed to get RSTR.xml");
- }
- BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(is));
- StringBuilder stringBuilder = new StringBuilder();
- String line = null;
- while ((line = bufferedReader.readLine()) != null) {
- stringBuilder.append(line + "\n");
- }
- bufferedReader.close();
- return stringBuilder.toString();
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- private static FederationContext loadRootConfig() {
+ private static FederationConfigurator getFederationConfigurator() {
+ if (configurator != null) {
+ return configurator;
+ }
try {
- FederationConfigurator configurator = new FederationConfigurator();
+ configurator = new FederationConfigurator();
final URL resource = Thread.currentThread().getContextClassLoader()
.getResource(CONFIG_FILE);
File f = new File(resource.toURI());
configurator.loadConfig(f);
- return configurator.getFederationContext("ROOT");
+ return configurator;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
- private static FederationContext loadOtherIssuerRootConfig() {
- try {
- FederationConfigurator configurator = new FederationConfigurator();
- final URL resource = Thread.currentThread().getContextClassLoader()
- .getResource(CONFIG_FILE_WRONG_ISSUER);
- File f = new File(resource.toURI());
- configurator.loadConfig(f);
- return configurator.getFederationContext("ROOT");
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * Roles are encoded as a multi-value saml attribute
+ */
+ @org.junit.Test
+ public void validateSAML2Token() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+ String rstr = createSamlToken(assertion, "mystskey");
+
+ FederationRequest wfReq = new FederationRequest();
+ wfReq.setWa(FederationConstants.ACTION_SIGNIN);
+ wfReq.setWresult(rstr);
+
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT");
+
+ FederationProcessor wfProc = new FederationProcessorImpl();
+ FederationResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER,
+ wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
+ Assert.assertEquals("Audience wrong", TEST_AUDIENCE, wfRes.getAudience());
}
-
+
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * Roles are encoded as a multiple saml attributes with the same name
+ */
@org.junit.Test
- public void validateSAML2Token() {
-
+ public void validateSAML2TokenRoleMultiAttributes() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ callbackHandler.setMultiValueType(MultiValue.MULTI_ATTR);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+ String rstr = createSamlToken(assertion, "mystskey");
+
FederationRequest wfReq = new FederationRequest();
wfReq.setWa(FederationConstants.ACTION_SIGNIN);
- wfReq.setWresult(sRSTR);
- FederationContext config = loadRootConfig();
- config.setDetectReplayedTokens(false);
- config.setDetectExpiredTokens(false);
+ wfReq.setWresult(rstr);
+
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT");
FederationProcessor wfProc = new FederationProcessorImpl();
FederationResponse wfRes = wfProc.processRequest(wfReq, config);
+
Assert.assertEquals("Principal name wrong", TEST_USER,
- wfRes.getUsername());
+ wfRes.getUsername());
Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
}
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * Roles are encoded as a single saml attribute with encoded value
+ */
@org.junit.Test
- public void validateSAML2TokenWithWrongIssuer() {
+ public void validateSAML2TokenRoleEncodedValue() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ callbackHandler.setMultiValueType(MultiValue.ENC_VALUE);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+ String rstr = createSamlToken(assertion, "mystskey");
+
+ FederationRequest wfReq = new FederationRequest();
+ wfReq.setWa(FederationConstants.ACTION_SIGNIN);
+ wfReq.setWresult(rstr);
+
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT");
+ FederationProtocol fp = (FederationProtocol)config.getProtocol();
+ fp.setRoleDelimiter(",");
+ FederationProcessor wfProc = new FederationProcessorImpl();
+ FederationResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER,
+ wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
+ }
+
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * The configured subject of the trusted issuer doesn't match with
+ * the issuer of the SAML token
+ */
+ @org.junit.Test
+ public void validateSAML2TokenUntrustedIssuer() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ String rstr = createSamlToken(assertion, "mystskey");
FederationRequest wfReq = new FederationRequest();
wfReq.setWa(FederationConstants.ACTION_SIGNIN);
- wfReq.setWresult(sRSTR);
- FederationContext config = loadOtherIssuerRootConfig();
- config.setDetectReplayedTokens(false);
- config.setDetectExpiredTokens(false);
+ wfReq.setWresult(rstr);
+
+ // Load and update the config to enforce an error
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT");
+ config.getTrustedIssuers().get(0).setSubject("wrong-issuer-name");
+
FederationProcessor wfProc = new FederationProcessorImpl();
try {
wfProc.processRequest(wfReq, config);
Assert.fail("Processing must fail because of wrong issuer configured");
} catch (RuntimeException ex) {
- Assert.assertEquals("Exception expected", "Issuer '"
- + TEST_RSTR_ISSUER + "' not trusted", ex.getMessage());
+ // expected
}
}
+ /**
+ * Validate SAML 2 token twice which causes an exception
+ * due to replay attack
+ */
@org.junit.Test
- public void validateSAML2TokenForRoles() {
-
+ public void testReplayAttack() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+ String rstr = createSamlToken(assertion, "mystskey");
+
FederationRequest wfReq = new FederationRequest();
wfReq.setWa(FederationConstants.ACTION_SIGNIN);
- wfReq.setWresult(sRSTR);
-
- FederationContext config = loadRootConfig();
- config.setDetectReplayedTokens(false);
- config.setDetectExpiredTokens(false);
+ wfReq.setWresult(rstr);
+
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT");
FederationProcessor wfProc = new FederationProcessorImpl();
FederationResponse wfRes = wfProc.processRequest(wfReq, config);
Assert.assertEquals("Principal name wrong", TEST_USER,
wfRes.getUsername());
Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
- Assert.assertEquals("One role must be found", 1, wfRes.getRoles()
- .size());
+
+ wfProc = new FederationProcessorImpl();
+ try {
+ wfProc.processRequest(wfReq, config);
+ fail("Failure expected on a replay attack");
+ } catch (Exception ex) {
+ // expected
+ }
}
+
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * The configured subject of the trusted issuer doesn't match with
+ * the issuer of the SAML token
+ */
@org.junit.Test
- public void testReplayAttack() {
-
+ public void validateSAML2TokenSeveralCertStore() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ String rstr = createSamlToken(assertion, "mystskey");
FederationRequest wfReq = new FederationRequest();
wfReq.setWa(FederationConstants.ACTION_SIGNIN);
- wfReq.setWresult(sRSTRREPLAY);
- FederationContext config = loadRootConfig();
- config.setDetectExpiredTokens(false);
+ wfReq.setWresult(rstr);
+
+ // Load and update the config to enforce an error
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT2");
+
+ FederationProcessor wfProc = new FederationProcessorImpl();
+ FederationResponse wfRes = wfProc.processRequest(wfReq, config);
+
+ Assert.assertEquals("Principal name wrong", TEST_USER,
+ wfRes.getUsername());
+ Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
+ }
+ /**
+ * Validate SAML 2 token which includes the role attribute with 2 values
+ * The configured subject of the trusted issuer doesn't match with
+ * the issuer of the SAML token
+ */
+ @org.junit.Test
+ public void validateSAML2TokenSeveralCertStoreTrustedIssuer() throws Exception {
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setStatement(SAML2CallbackHandler.Statement.ATTR);
+ callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
+ callbackHandler.setIssuer(TEST_RSTR_ISSUER);
+ callbackHandler.setSubjectName(TEST_USER);
+ ConditionsBean cp = new ConditionsBean();
+ cp.setAudienceURI(TEST_AUDIENCE);
+ callbackHandler.setConditions(cp);
+
+ SAMLParms samlParms = new SAMLParms();
+ samlParms.setCallbackHandler(callbackHandler);
+ AssertionWrapper assertion = new AssertionWrapper(samlParms);
+
+ String rstr = createSamlToken(assertion, "mystskey");
+ FederationRequest wfReq = new FederationRequest();
+ wfReq.setWa(FederationConstants.ACTION_SIGNIN);
+ wfReq.setWresult(rstr);
+
+ // Load and update the config to enforce an error
+ configurator = null;
+ FederationContext config = getFederationConfigurator().getFederationContext("ROOT3");
+
FederationProcessor wfProc = new FederationProcessorImpl();
FederationResponse wfRes = wfProc.processRequest(wfReq, config);
+
Assert.assertEquals("Principal name wrong", TEST_USER,
- wfRes.getUsername());
+ wfRes.getUsername());
Assert.assertEquals("Issuer wrong", TEST_RSTR_ISSUER, wfRes.getIssuer());
+ Assert.assertEquals("Two roles must be found", 2, wfRes.getRoles()
+ .size());
+ }
+
+
+ private String createSamlToken(AssertionWrapper assertion, String alias) throws IOException,
+ UnsupportedCallbackException, WSSecurityException, Exception {
+ WSPasswordCallback[] cb = {new WSPasswordCallback(alias, WSPasswordCallback.SIGNATURE)};
+ cbPasswordHandler.handle(cb);
+ String password = cb[0].getPassword();
- wfProc = new FederationProcessorImpl();
- try {
- wfProc.processRequest(wfReq, config);
- fail("Failure expected on a replay attack");
- } catch (Exception ex) {
- // expected
+ assertion.signAssertion(alias, password, crypto, false);
+ Document doc = STSUtil.toSOAPPart(STSUtil.SAMPLE_RSTR_COLL_MSG);
+ Element token = assertion.toDOM(doc);
+
+ Element e = FederationProcessorTest.findElement(doc, "RequestedSecurityToken",
+ FederationConstants.WS_TRUST_13_NS);
+ e.appendChild(token);
+ return DOM2Writer.nodeToString(doc);
+ }
+
+
+
+
+ /**
+ * Returns the first element that matches <code>name</code> and
+ * <code>namespace</code>. <p/> This is a replacement for a XPath lookup
+ * <code>//name</code> with the given namespace. It's somewhat faster than
+ * XPath, and we do not deal with prefixes, just with the real namespace URI
+ *
+ * @param startNode Where to start the search
+ * @param name Local name of the element
+ * @param namespace Namespace URI of the element
+ * @return The found element or <code>null</code>
+ */
+ public static Element findElement(Node startNode, String name, String namespace) {
+ //
+ // Replace the formerly recursive implementation with a depth-first-loop
+ // lookup
+ //
+ if (startNode == null) {
+ return null;
}
+ Node startParent = startNode.getParentNode();
+ Node processedNode = null;
+
+ while (startNode != null) {
+ // start node processing at this point
+ if (startNode.getNodeType() == Node.ELEMENT_NODE
+ && startNode.getLocalName().equals(name)) {
+ String ns = startNode.getNamespaceURI();
+ if (ns != null && ns.equals(namespace)) {
+ return (Element)startNode;
+ }
+
+ if ((namespace == null || namespace.length() == 0)
+ && (ns == null || ns.length() == 0)) {
+ return (Element)startNode;
+ }
+ }
+ processedNode = startNode;
+ startNode = startNode.getFirstChild();
+
+ // no child, this node is done.
+ if (startNode == null) {
+ // close node processing, get sibling
+ startNode = processedNode.getNextSibling();
+ }
+ // no more siblings, get parent, all children
+ // of parent are processed.
+ while (startNode == null) {
+ processedNode = processedNode.getParentNode();
+ if (processedNode == startParent) {
+ return null;
+ }
+ // close parent node processing (processed node now)
+ startNode = processedNode.getNextSibling();
+ }
+ }
+ return null;
}
+
}
Added: cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/KeystoreCallbackHandler.java
URL: http://svn.apache.org/viewvc/cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/KeystoreCallbackHandler.java?rev=1345768&view=auto
==============================================================================
--- cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/KeystoreCallbackHandler.java (added)
+++ cxf/fediz/trunk/plugins/core/src/test/java/org/apache/cxf/fediz/core/KeystoreCallbackHandler.java Sun Jun 3 20:25:34 2012
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.core;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.ws.security.WSPasswordCallback;
+
+/**
+ * A Callback Handler implementation for the case of finding a password to access a
+ * cert/private key in a keystore.
+ */
+public class KeystoreCallbackHandler implements CallbackHandler {
+
+ private Map<String, String> users = new HashMap<String, String>();
+
+ public KeystoreCallbackHandler() {
+ users.put("mystskey", "stskpass");
+ users.put("realma", "realma");
+ users.put("realmb", "realmb");
+ }
+
+ public void handle(Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ for (int i = 0; i < callbacks.length; i++) {
+ if (callbacks[i] instanceof WSPasswordCallback) {
+ WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
+ pc.setPassword(users.get(pc.getIdentifier()));
+ } else {
+ throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
+ }
+ }
+ }
+}