STSTokenValidatorTest.java
/**
* 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.ws.security.trust;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.ws.security.wss4j.saml.SAML2CallbackHandler;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoFactory;
import org.apache.wss4j.common.saml.SAMLCallback;
import org.apache.wss4j.common.saml.SAMLUtil;
import org.apache.wss4j.common.saml.SamlAssertionWrapper;
import org.apache.wss4j.dom.engine.WSSConfig;
import org.apache.wss4j.dom.handler.RequestData;
import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor;
import org.apache.wss4j.dom.validate.Credential;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Tests for local validation of SAML Assertions in the STSTokenValidator.
*/
public class STSTokenValidatorTest {
private static Crypto signingCrypto;
private static Crypto trustedCrypto;
private static Crypto untrustedCrypto;
@BeforeClass
public static void init() throws Exception {
WSSConfig.init();
signingCrypto = CryptoFactory.getInstance("outsecurity.properties");
// Contains the signing certificate
trustedCrypto = CryptoFactory.getInstance("outsecurity.properties");
// Only trusts an unrelated CA
untrustedCrypto = CryptoFactory.getInstance("cxfca.properties");
}
@Test
public void testTrustedSignedAssertion() throws Exception {
STSTokenValidator validator = new STSTokenValidator();
RequestData data = createRequestData(trustedCrypto);
assertTrue(validator.isValidatedLocally(createCredential(true, data), data));
}
@Test
public void testUntrustedSignedAssertion() throws Exception {
STSTokenValidator validator = new STSTokenValidator();
RequestData data = createRequestData(untrustedCrypto);
assertFalse(validator.isValidatedLocally(createCredential(true, data), data));
}
/**
* An unsigned Assertion must not be treated as validated locally because a previous
* signed Assertion was trusted.
*/
@Test
public void testUnsignedAssertionAfterTrustedAssertion() throws Exception {
STSTokenValidator validator = new STSTokenValidator();
RequestData trustedData = createRequestData(trustedCrypto);
assertTrue(validator.isValidatedLocally(createCredential(true, trustedData), trustedData));
RequestData data = createRequestData(trustedCrypto);
assertFalse(validator.isValidatedLocally(createCredential(false, data), data));
}
/**
* An untrusted Assertion must never be treated as validated locally, even when a trusted
* Assertion is validated concurrently by the same STSTokenValidator.
*/
@Test
public void testConcurrentTrustedAndUntrustedAssertions() throws Exception {
final STSTokenValidator validator = new STSTokenValidator();
final int iterations = 200;
final RequestData trustedData = createRequestData(trustedCrypto);
final Credential trustedCredential = createCredential(true, trustedData);
final RequestData untrustedData = createRequestData(untrustedCrypto);
final Credential untrustedCredential = createCredential(true, untrustedData);
ExecutorService executor = Executors.newFixedThreadPool(2);
try {
Callable<Boolean> trusted = () -> {
boolean result = true;
for (int i = 0; i < iterations; i++) {
result &= validator.isValidatedLocally(trustedCredential, trustedData);
}
return result;
};
Callable<Boolean> untrusted = () -> {
boolean result = false;
for (int i = 0; i < iterations; i++) {
result |= validator.isValidatedLocally(untrustedCredential, untrustedData);
}
return result;
};
Future<Boolean> trustedResult = executor.submit(trusted);
Future<Boolean> untrustedResult = executor.submit(untrusted);
assertTrue(trustedResult.get(60, TimeUnit.SECONDS));
assertFalse(untrustedResult.get(60, TimeUnit.SECONDS));
} finally {
executor.shutdownNow();
}
}
private static RequestData createRequestData(Crypto sigVerCrypto) {
RequestData data = new RequestData();
data.setSigVerCrypto(sigVerCrypto);
data.setWssConfig(WSSConfig.getNewInstance());
return data;
}
private static Credential createCredential(boolean signed, RequestData data) throws Exception {
SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
SAMLCallback samlCallback = new SAMLCallback();
SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
if (signed) {
assertion.signAssertion("myalias", "myAliasPassword", signingCrypto, false);
}
// Round-trip the Assertion via DOM, as would happen on receipt of a message
Document doc = DOMUtils.createDocument();
Element element = assertion.toDOM(doc);
// The Assertion must be attached to the Document so that the signature Reference can be resolved
doc.appendChild(element);
SamlAssertionWrapper receivedAssertion = new SamlAssertionWrapper(element);
if (signed) {
receivedAssertion.verifySignature(new WSSSAMLKeyInfoProcessor(data), data.getSigVerCrypto());
}
Credential credential = new Credential();
credential.setSamlAssertion(receivedAssertion);
return credential;
}
}