Author: coheigea
Date: Thu Nov 24 15:49:18 2011
New Revision: 1205892
URL: http://svn.apache.org/viewvc?rev=1205892&view=rev
Log:
[CXF-3924] - Support to configure keystore per SAML realm
- Patch applied, thanks.
Modified:
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java
cxf/trunk/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java
Modified:
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java?rev=1205892&r1=1205891&r2=1205892&view=diff
==============================================================================
---
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
(original)
+++
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
Thu Nov 24 15:49:18 2011
@@ -28,6 +28,8 @@ import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.security.auth.callback.CallbackHandler;
+
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -35,6 +37,7 @@ import org.apache.cxf.common.logging.Log
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.sts.STSConstants;
import org.apache.cxf.sts.STSPropertiesMBean;
+import org.apache.cxf.sts.SignatureProperties;
import org.apache.cxf.sts.request.KeyRequirements;
import org.apache.cxf.sts.request.TokenRequirements;
import org.apache.cxf.sts.token.realm.SAMLRealm;
@@ -287,29 +290,41 @@ public class SAMLTokenProvider implement
if (signToken) {
STSPropertiesMBean stsProperties =
tokenParameters.getStsProperties();
- String alias = null;
+ // Initialise signature objects with defaults of STSPropertiesMBean
+ Crypto signatureCrypto = stsProperties.getSignatureCrypto();
+ CallbackHandler callbackHandler =
stsProperties.getCallbackHandler();
+ SignatureProperties signatureProperties =
stsProperties.getSignatureProperties();
+ String alias = stsProperties.getSignatureUsername();
+
if (samlRealm != null) {
- alias = samlRealm.getSignatureAlias();
- }
- if (alias == null || "".equals(alias)) {
- alias = stsProperties.getSignatureUsername();
- }
- if (alias == null || "".equals(alias)) {
- Crypto signatureCrypto = stsProperties.getSignatureCrypto();
- if (signatureCrypto != null) {
- alias = signatureCrypto.getDefaultX509Identifier();
- LOG.fine("Signature alias is null so using default alias:
" + alias);
+ // If SignatureCrypto configured in realm then
+ // callbackhandler and alias of STSPropertiesMBean is ignored
+ if (samlRealm.getSignatureCrypto() != null) {
+ LOG.fine("SAMLRealm signature keystore used");
+ signatureCrypto = samlRealm.getSignatureCrypto();
+ callbackHandler = samlRealm.getCallbackHandler();
+ alias = samlRealm.getSignatureAlias();
+ }
+ // SignatureProperties can be defined independently of
SignatureCrypto
+ if (samlRealm.getSignatureProperties() != null) {
+ signatureProperties = samlRealm.getSignatureProperties();
}
+ }
+
+ // If alias not defined, get the default of the SignatureCrypto
+ if ((alias == null || "".equals(alias)) && (signatureCrypto !=
null)) {
+ alias = signatureCrypto.getDefaultX509Identifier();
+ LOG.fine("Signature alias is null so using default alias: " +
alias);
}
// Get the password
WSPasswordCallback[] cb = {new WSPasswordCallback(alias,
WSPasswordCallback.SIGNATURE)};
LOG.fine("Creating SAML Token");
- stsProperties.getCallbackHandler().handle(cb);
+ callbackHandler.handle(cb);
String password = cb[0].getPassword();
LOG.fine("Signing SAML Token");
- boolean useKeyValue =
stsProperties.getSignatureProperties().isUseKeyValue();
- assertion.signAssertion(alias, password,
stsProperties.getSignatureCrypto(), useKeyValue);
+ boolean useKeyValue = signatureProperties.isUseKeyValue();
+ assertion.signAssertion(alias, password, signatureCrypto,
useKeyValue);
}
return assertion;
Modified:
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java?rev=1205892&r1=1205891&r2=1205892&view=diff
==============================================================================
---
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java
(original)
+++
cxf/trunk/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/realm/SAMLRealm.java
Thu Nov 24 15:49:18 2011
@@ -19,15 +19,43 @@
package org.apache.cxf.sts.token.realm;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.classloader.ClassLoaderUtils;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.resource.ResourceManager;
+import org.apache.cxf.sts.SignatureProperties;
+import org.apache.cxf.sts.StaticSTSProperties;
+import org.apache.cxf.ws.security.sts.provider.STSException;
+import org.apache.ws.security.WSSecurityException;
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+
/**
* This class defines some properties that are associated with a realm for the
SAMLTokenProvider and
* SAMLTokenValidator.
*/
public class SAMLRealm {
+
+ private static final Logger LOG = LogUtils.getL7dLogger(SAMLRealm.class);
+
private String issuer;
private String signatureAlias;
-
+ private Crypto signatureCrypto;
+ private SignatureProperties signatureProperties;
+ private String signaturePropertiesFile;
+ private String callbackHandlerClass;
+ private CallbackHandler callbackHandler;
+
/**
* Get the issuer of this SAML realm
* @return the issuer of this SAML realm
@@ -60,4 +88,151 @@ public class SAMLRealm {
this.signatureAlias = signatureAlias;
}
+ /**
+ * Set the signature Crypto object
+ * @param signatureCrypto the signature Crypto object
+ */
+ public void setSignatureCrypto(Crypto signatureCrypto) {
+ this.signatureCrypto = signatureCrypto;
+ }
+
+ /**
+ * Set the String corresponding to the signature Properties class
+ * @param signaturePropertiesFile the String corresponding to the
signature properties file
+ */
+ public void setSignaturePropertiesFile(String signaturePropertiesFile) {
+ this.signaturePropertiesFile = signaturePropertiesFile;
+ LOG.fine("Setting signature properties: " + signaturePropertiesFile);
+ }
+
+ /**
+ * Set the SignatureProperties to use.
+ * @param signatureProperties the SignatureProperties to use.
+ */
+ public void setSignatureProperties(SignatureProperties
signatureProperties) {
+ this.signatureProperties = signatureProperties;
+ }
+
+ /**
+ * Get the SignatureProperties to use.
+ * @return the SignatureProperties to use.
+ */
+ public SignatureProperties getSignatureProperties() {
+ return signatureProperties;
+ }
+
+
+ /**
+ * Get the signature Crypto object
+ * @return the signature Crypto object
+ */
+ public Crypto getSignatureCrypto() {
+ if (signatureCrypto == null && signaturePropertiesFile != null) {
+ Properties sigProperties = getProps(signaturePropertiesFile);
+ if (sigProperties == null) {
+ LOG.fine("Cannot load signature properties using: " +
signaturePropertiesFile);
+ throw new STSException("Configuration error: cannot load
signature properties");
+ }
+ try {
+ signatureCrypto = CryptoFactory.getInstance(sigProperties);
+ } catch (WSSecurityException ex) {
+ LOG.fine("Error in loading the signature Crypto object: " +
ex.getMessage());
+ throw new STSException(ex.getMessage());
+ }
+ }
+
+ return signatureCrypto;
+ }
+
+
+ /**
+ * Set the CallbackHandler object.
+ * @param callbackHandler the CallbackHandler object.
+ */
+ public void setCallbackHandler(CallbackHandler callbackHandler) {
+ this.callbackHandler = callbackHandler;
+ LOG.fine("Setting callbackHandler: " + callbackHandler);
+ }
+
+ /**
+ * Set the String corresponding to the CallbackHandler class.
+ * @param callbackHandlerClass the String corresponding to the
CallbackHandler class.
+ */
+ public void setCallbackHandlerClass(String callbackHandlerClass) {
+ this.callbackHandlerClass = callbackHandlerClass;
+ LOG.fine("Setting callbackHandlerClass: " + callbackHandlerClass);
+ }
+
+ /**
+ * Get the CallbackHandler object.
+ * @return the CallbackHandler object.
+ */
+ public CallbackHandler getCallbackHandler() {
+ if (callbackHandler == null && callbackHandlerClass != null) {
+ callbackHandler = getCallbackHandler(callbackHandlerClass);
+ if (callbackHandler == null) {
+ LOG.fine("Cannot load CallbackHandler using: " +
callbackHandlerClass);
+ throw new STSException("Configuration error: cannot load
callback handler");
+ }
+ }
+ return callbackHandler;
+ }
+
+ private static Properties getProps(Object o) {
+ Properties properties = null;
+ if (o instanceof Properties) {
+ properties = (Properties)o;
+ } else if (o instanceof String) {
+ URL url = null;
+ Bus bus =
PhaseInterceptorChain.getCurrentMessage().getExchange().getBus();
+ ResourceManager rm = bus.getExtension(ResourceManager.class);
+ url = rm.resolveResource((String)o, URL.class);
+ try {
+ if (url == null) {
+ url = ClassLoaderUtils.getResource((String)o,
StaticSTSProperties.class);
+ }
+ if (url == null) {
+ url = new URL((String)o);
+ }
+ if (url != null) {
+ properties = new Properties();
+ InputStream ins = url.openStream();
+ properties.load(ins);
+ ins.close();
+ }
+ } catch (IOException e) {
+ LOG.fine(e.getMessage());
+ properties = null;
+ }
+ } else if (o instanceof URL) {
+ properties = new Properties();
+ try {
+ InputStream ins = ((URL)o).openStream();
+ properties.load(ins);
+ ins.close();
+ } catch (IOException e) {
+ LOG.fine(e.getMessage());
+ properties = null;
+ }
+ }
+ return properties;
+ }
+
+ private CallbackHandler getCallbackHandler(Object o) {
+ CallbackHandler handler = null;
+ if (o instanceof CallbackHandler) {
+ handler = (CallbackHandler)o;
+ } else if (o instanceof String) {
+ try {
+ handler =
+ (CallbackHandler)ClassLoaderUtils.loadClass((String)o,
this.getClass()).newInstance();
+ } catch (Exception e) {
+ LOG.fine(e.getMessage());
+ handler = null;
+ }
+ }
+ return handler;
+ }
+
+
}
Modified:
cxf/trunk/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java?rev=1205892&r1=1205891&r2=1205892&view=diff
==============================================================================
---
cxf/trunk/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java
(original)
+++
cxf/trunk/services/sts/sts-core/src/test/java/org/apache/cxf/sts/operation/IssueSamlRealmUnitTest.java
Thu Nov 24 15:49:18 2011
@@ -47,6 +47,7 @@ import org.apache.cxf.sts.service.Static
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.sts.token.provider.TokenProvider;
import org.apache.cxf.sts.token.realm.SAMLRealm;
+import org.apache.cxf.ws.security.sts.provider.STSException;
import
org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseCollectionType;
import
org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType;
@@ -297,6 +298,187 @@ public class IssueSamlRealmUnitTest exte
assertTrue(tokenString.contains("STS"));
}
+
+ /**
+ * Test to successfully issue a Saml 1.1 token in realm "B"
+ * using crypto definition in SAMLRealm
+ */
+ @org.junit.Test
+ public void testIssueSaml1TokenRealmBCustomCrypto() throws Exception {
+ TokenIssueOperation issueOperation = new TokenIssueOperation();
+
+ // Add Token Provider
+ List<TokenProvider> providerList = new ArrayList<TokenProvider>();
+ SAMLTokenProvider provider = new SAMLTokenProvider();
+ provider.setRealmMap(createRealms());
+ providerList.add(provider);
+ issueOperation.setTokenProviders(providerList);
+
+ // Add Service
+ ServiceMBean service = new StaticService();
+
service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy"));
+ issueOperation.setServices(Collections.singletonList(service));
+
+ // Add STSProperties object
+ STSPropertiesMBean stsProperties = new StaticSTSProperties();
+ Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties());
+ stsProperties.setEncryptionCrypto(crypto);
+ stsProperties.setSignatureCrypto(crypto);
+ stsProperties.setEncryptionUsername("myservicekey");
+ stsProperties.setSignatureUsername("mystskey");
+ stsProperties.setCallbackHandler(new PasswordCallbackHandler());
+ stsProperties.setIssuer("STS");
+ stsProperties.setRealmParser(new CustomRealmParser());
+ issueOperation.setStsProperties(stsProperties);
+
+ // Set signature properties in SAMLRealm B
+ Map<String, SAMLRealm> samlRealms = provider.getRealmMap();
+ SAMLRealm realm = samlRealms.get("B");
+ realm.setSignatureCrypto(crypto);
+ realm.setCallbackHandler(new PasswordCallbackHandler());
+
+
+ // Mock up a request
+ RequestSecurityTokenType request = new RequestSecurityTokenType();
+ JAXBElement<String> tokenType =
+ new JAXBElement<String>(
+ QNameConstants.TOKEN_TYPE, String.class,
WSConstants.WSS_SAML_TOKEN_TYPE
+ );
+ request.getAny().add(tokenType);
+
request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy"));
+
+ // Mock up message context
+ MessageImpl msg = new MessageImpl();
+ WrappedMessageContext msgCtx = new WrappedMessageContext(msg);
+ msgCtx.put("url", "https");
+ msgCtx.put(
+ SecurityContext.class.getName(),
+ createSecurityContext(new CustomTokenPrincipal("alice"))
+ );
+ WebServiceContextImpl webServiceContext = new
WebServiceContextImpl(msgCtx);
+
+ // Issue a token - this will fail as the SAMLRealm configuration is
inconsistent
+ // no signature alias defined
+ try {
+ issueOperation.issue(request, webServiceContext);
+ fail("Failure expected on no encryption name");
+ } catch (STSException ex) {
+ // expected
+ }
+
+ realm.setSignatureAlias("mystskey");
+
+ // Issue a token
+ RequestSecurityTokenResponseCollectionType response =
+ issueOperation.issue(request, webServiceContext);
+ List<RequestSecurityTokenResponseType> securityTokenResponse =
+ response.getRequestSecurityTokenResponse();
+ assertTrue(!securityTokenResponse.isEmpty());
+
+ // Test the generated token.
+ Element assertion = null;
+ for (Object tokenObject : securityTokenResponse.get(0).getAny()) {
+ if (tokenObject instanceof JAXBElement<?>
+ &&
REQUESTED_SECURITY_TOKEN.equals(((JAXBElement<?>)tokenObject).getName())) {
+ RequestedSecurityTokenType rstType =
+
(RequestedSecurityTokenType)((JAXBElement<?>)tokenObject).getValue();
+ assertion = (Element)rstType.getAny();
+ break;
+ }
+ }
+
+ assertNotNull(assertion);
+ String tokenString = DOM2Writer.nodeToString(assertion);
+ assertFalse(tokenString.contains("A-Issuer"));
+ assertTrue(tokenString.contains("B-Issuer"));
+ assertFalse(tokenString.contains("STS"));
+ }
+
+
+ /**
+ * Test to successfully issue a Saml 1.1 token in realm "B"
+ * using PKCS12 crypto definition with default key in SAMLRealm
+ */
+ @org.junit.Test
+ public void testIssueSaml1TokenRealmBCustomCryptoPKCS12() throws Exception
{
+ TokenIssueOperation issueOperation = new TokenIssueOperation();
+
+ // Add Token Provider
+ List<TokenProvider> providerList = new ArrayList<TokenProvider>();
+ SAMLTokenProvider provider = new SAMLTokenProvider();
+ provider.setRealmMap(createRealms());
+ providerList.add(provider);
+ issueOperation.setTokenProviders(providerList);
+
+ // Add Service
+ ServiceMBean service = new StaticService();
+
service.setEndpoints(Collections.singletonList("http://dummy-service.com/dummy"));
+ issueOperation.setServices(Collections.singletonList(service));
+
+ // Add STSProperties object
+ STSPropertiesMBean stsProperties = new StaticSTSProperties();
+ Crypto crypto = CryptoFactory.getInstance(getEncryptionProperties());
+ stsProperties.setEncryptionCrypto(crypto);
+ stsProperties.setSignatureCrypto(crypto);
+ stsProperties.setEncryptionUsername("myservicekey");
+ stsProperties.setSignatureUsername("mystskey");
+ stsProperties.setCallbackHandler(new PasswordCallbackHandler());
+ stsProperties.setIssuer("STS");
+ stsProperties.setRealmParser(new CustomRealmParser());
+ issueOperation.setStsProperties(stsProperties);
+
+ // Set signature properties in SAMLRealm B
+ Map<String, SAMLRealm> samlRealms = provider.getRealmMap();
+ SAMLRealm realm = samlRealms.get("B");
+
realm.setSignatureCrypto(CryptoFactory.getInstance(getEncryptionPropertiesPKCS12()));
+ realm.setCallbackHandler(new PasswordCallbackHandler());
+
+ // Mock up a request
+ RequestSecurityTokenType request = new RequestSecurityTokenType();
+ JAXBElement<String> tokenType =
+ new JAXBElement<String>(
+ QNameConstants.TOKEN_TYPE, String.class,
WSConstants.WSS_SAML_TOKEN_TYPE
+ );
+ request.getAny().add(tokenType);
+
request.getAny().add(createAppliesToElement("http://dummy-service.com/dummy"));
+
+ // Mock up message context
+ MessageImpl msg = new MessageImpl();
+ WrappedMessageContext msgCtx = new WrappedMessageContext(msg);
+ msgCtx.put("url", "https");
+ msgCtx.put(
+ SecurityContext.class.getName(),
+ createSecurityContext(new CustomTokenPrincipal("alice"))
+ );
+ WebServiceContextImpl webServiceContext = new
WebServiceContextImpl(msgCtx);
+
+ // Issue a token
+ RequestSecurityTokenResponseCollectionType response =
+ issueOperation.issue(request, webServiceContext);
+ List<RequestSecurityTokenResponseType> securityTokenResponse =
+ response.getRequestSecurityTokenResponse();
+ assertTrue(!securityTokenResponse.isEmpty());
+
+ // Test the generated token.
+ Element assertion = null;
+ for (Object tokenObject : securityTokenResponse.get(0).getAny()) {
+ if (tokenObject instanceof JAXBElement<?>
+ &&
REQUESTED_SECURITY_TOKEN.equals(((JAXBElement<?>)tokenObject).getName())) {
+ RequestedSecurityTokenType rstType =
+
(RequestedSecurityTokenType)((JAXBElement<?>)tokenObject).getValue();
+ assertion = (Element)rstType.getAny();
+ break;
+ }
+ }
+
+ assertNotNull(assertion);
+ String tokenString = DOM2Writer.nodeToString(assertion);
+ assertFalse(tokenString.contains("A-Issuer"));
+ assertTrue(tokenString.contains("B-Issuer"));
+ assertFalse(tokenString.contains("STS"));
+ }
+
+
/**
* Create some SAML Realms
*/
@@ -354,5 +536,18 @@ public class IssueSamlRealmUnitTest exte
return properties;
}
+ private Properties getEncryptionPropertiesPKCS12() {
+ Properties properties = new Properties();
+ properties.put(
+ "org.apache.ws.security.crypto.provider",
"org.apache.ws.security.components.crypto.Merlin"
+ );
+
properties.put("org.apache.ws.security.crypto.merlin.keystore.password",
"security");
+ properties.put("org.apache.ws.security.crypto.merlin.keystore.file",
"x509.p12");
+ properties.put("org.apache.ws.security.crypto.merlin.keystore.type",
"pkcs12");
+
properties.put("org.apache.ws.security.crypto.merlin.keystore.private.password",
"security");
+
+ return properties;
+ }
+
}