/*
* JBoss, Home of Professional Open Source
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.picketlink.identity.federation.core.wstrust;
import org.picketlink.common.PicketLinkLogger;
import org.picketlink.common.PicketLinkLoggerFactory;
import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.config.federation.AuthPropertyType;
import org.picketlink.config.federation.ClaimsProcessorType;
import org.picketlink.config.federation.ClaimsProcessorsType;
import org.picketlink.config.federation.KeyProviderType;
import org.picketlink.config.federation.KeyValueType;
import org.picketlink.config.federation.STSType;
import org.picketlink.config.federation.ServiceProviderType;
import org.picketlink.config.federation.ServiceProvidersType;
import org.picketlink.config.federation.TokenProviderType;
import org.picketlink.config.federation.TokenProvidersType;
import org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider;
import org.picketlink.identity.federation.core.interfaces.TrustKeyManager;
import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS;
import org.picketlink.identity.federation.core.sts.STSCoreConfig;
import org.picketlink.identity.federation.core.util.CoreConfigUtil;
import javax.xml.namespace.QName;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* <p>
* Standard JBoss STS configuration implementation.
* </p>
*
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
* @author <a href="mailto:asaldhan@redhat.com">Anil Saldhana</a>
*/
public class PicketLinkSTSConfiguration implements STSConfiguration {
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
// the delegate contains all the information extracted from the picketlink-sts.xml configuration file.
private final STSType delegate;
private final Map<String, SecurityTokenProvider> tokenProviders = new HashMap<String, SecurityTokenProvider>();
private final Map<String, ServiceProviderType> spMetadata = new HashMap<String, ServiceProviderType>();
private final Map<String, ServiceProviderType> spRegExMetadata = new HashMap<String, ServiceProviderType>();
private final Map<String, ClaimsProcessor> claimsProcessors = new HashMap<String, ClaimsProcessor>();
private TrustKeyManager trustManager;
private WSTrustRequestHandler handler;
private String certificateAlias;
/**
* <p>
* Creates an instance of {@code PicketLinkSTSConfiguration} with default configuration values.
* </p>
*/
public PicketLinkSTSConfiguration() {
this.delegate = new STSType();
this.delegate.setRequestHandler(StandardRequestHandler.class.getCanonicalName());
// TODO: add default token provider classes.
}
/**
* <p>
* Creates an instance of {@code PicketLinkSTSConfiguration} with the specified configuration.
* </p>
*
* @param config a reference to the object that holds the configuration of the STS.
*/
public PicketLinkSTSConfiguration(STSType config) {
this.delegate = config;
// set the default request handler if one hasn't been specified.
if (this.delegate.getRequestHandler() == null)
this.delegate.setRequestHandler(StandardRequestHandler.class.getCanonicalName());
// build the token-provider maps.
TokenProvidersType providers = this.delegate.getTokenProviders();
if (providers != null) {
for (TokenProviderType provider : providers.getTokenProvider()) {
// get the properties that have been configured for the token provider.
Map<String, String> properties = new HashMap<String, String>();
List<KeyValueType> providerPropertiesList;
try {
providerPropertiesList = CoreConfigUtil.getProperties(provider);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
properties.put(GeneralConstants.ASSERTIONS_VALIDITY, String.valueOf(config.getTokenTimeout()));
properties.put(GeneralConstants.CLOCK_SKEW, String.valueOf(config.getClockSkew()));
for (KeyValueType propertyType : providerPropertiesList) {
properties.put(propertyType.getKey(), propertyType.getValue());
}
// create and initialize the token provider.
SecurityTokenProvider tokenProvider = WSTrustServiceFactory.getInstance().createTokenProvider(
provider.getProviderClass(), properties);
// token providers can be keyed by the token type and by token element + namespace.
this.tokenProviders.put(provider.getTokenType(), tokenProvider);
String tokenElementAndNS = tokenProvider.family() + "$" + provider.getTokenElement() + "$"
+ provider.getTokenElementNS();
this.tokenProviders.put(tokenElementAndNS, tokenProvider);
}
}
// build the claims processors map.
ClaimsProcessorsType processors = this.delegate.getClaimsProcessors();
if (processors != null) {
for (ClaimsProcessorType processor : processors.getClaimsProcessor()) {
// get the properties that have been configured for the claims processor.
Map<String, String> properties = new HashMap<String, String>();
List<KeyValueType> processorPropertiesList;
try {
processorPropertiesList = CoreConfigUtil.getProperties(processor);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
for (KeyValueType propertyType : processorPropertiesList)
properties.put(propertyType.getKey(), propertyType.getValue());
// create and initialize the claims processor.
ClaimsProcessor claimsProcessor = WSTrustServiceFactory.getInstance().createClaimsProcessor(
processor.getProcessorClass(), properties);
// store the processor using the dialect as the key.
this.claimsProcessors.put(processor.getDialect(), claimsProcessor);
}
}
// setup the service providers metadata.
//if the provider.getEndpoint() not null the provider will be added to spMetaData
//if the provider.getEndpointRegEx() not null the provider will be added to spRegExMetadata
ServiceProvidersType serviceProviders = this.delegate.getServiceProviders();
if (serviceProviders != null) {
for (ServiceProviderType provider : serviceProviders.getServiceProvider()) {
if (provider.getEndpoint() != null) {
this.spMetadata.put(provider.getEndpoint(), provider);
}
if (provider.getEndpointRegEx() != null) {
this.spRegExMetadata.put(provider.getEndpointRegEx(), provider);
}
}
}
// setup the key store.
KeyProviderType keyProviderType = config.getKeyProvider();
if (keyProviderType != null) {
String keyManagerClassName = keyProviderType.getClassName();
try {
// Decrypt/de-mask the passwords if any
List<AuthPropertyType> authProperties = CoreConfigUtil.getKeyProviderProperties(keyProviderType);
Class<?> clazz = SecurityActions.loadClass(getClass(), keyManagerClassName);
if (clazz == null)
throw logger.classNotLoadedError(keyManagerClassName);
this.trustManager = (TrustKeyManager) clazz.newInstance();
this.trustManager.setAuthProperties(authProperties);
this.trustManager.setValidatingAlias(keyProviderType.getValidatingAlias());
//Special case when you need X509Data in SignedInfo
if (authProperties != null) {
for (AuthPropertyType authPropertyType : authProperties) {
String key = authPropertyType.getKey();
if (GeneralConstants.X509CERTIFICATE.equals(key)) {
//we need X509Certificate in SignedInfo. The value is the alias name
trustManager.addAdditionalOption(GeneralConstants.X509CERTIFICATE, authPropertyType.getValue());
break;
}
}
}
} catch (Exception e) {
throw logger.stsUnableToConstructKeyManagerError(e);
}
}
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getSTSName()
*/
public String getSTSName() {
return this.delegate.getSTSName();
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getEncryptIssuedToken()
*/
public boolean encryptIssuedToken() {
return this.delegate.isEncryptToken();
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#signIssuedToken()
*/
public boolean signIssuedToken() {
return this.delegate.isSignToken();
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getIssuedTokenTimeout()
*/
public long getIssuedTokenTimeout() {
// return the timeout value in milliseconds.
return this.delegate.getTokenTimeout() * 1000;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getRequestHandlerClass()
*/
public WSTrustRequestHandler getRequestHandler() {
if (this.handler == null)
this.handler = WSTrustServiceFactory.getInstance().createRequestHandler(this.delegate.getRequestHandler(), this);
return this.handler;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getProviderForService(java.lang.String)
*/
public SecurityTokenProvider getProviderForService(String serviceName) {
if (serviceName == null)
throw logger.nullArgumentError("serviceName");
ServiceProviderType provider = this.spMetadata.get(serviceName);
if (provider != null) {
return this.tokenProviders.get(provider.getTokenType());
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getProviderForTokenType(java.lang.String)
*/
public SecurityTokenProvider getProviderForTokenType(String tokenType) {
if (tokenType == null)
throw logger.nullArgumentError("tokenType");
return this.tokenProviders.get(tokenType);
}
/**
* @see org.picketlink.identity.federation.core.sts.STSCoreConfig#getProviderForTokenElementNS(java.lang.String,
* javax.xml.namespace.QName)
*/
public SecurityTokenProvider getProviderForTokenElementNS(String family, QName tokenQName) {
return this.tokenProviders.get(family + "$" + tokenQName.getLocalPart() + "$" + tokenQName.getNamespaceURI());
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getClaimsProcessor(java.lang.String)
*/
public ClaimsProcessor getClaimsProcessor(String claimsDialect) {
return this.claimsProcessors.get(claimsDialect);
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getTokenTypeForService(java.lang.String)
*/
public String getTokenTypeForService(String serviceName) {
ServiceProviderType provider = this.spMetadata.get(serviceName);
if (provider != null)
return provider.getTokenType();
Set<String> keys = this.spRegExMetadata.keySet();
for (String next : keys) {
if (Pattern.matches(next, serviceName)) {
return this.spRegExMetadata.get(next).getTokenType();
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getServiceProviderPublicKey(java.lang.String)
*/
public PublicKey getServiceProviderPublicKey(String serviceName) {
PublicKey key = null;
if (this.trustManager != null) {
try {
// try using the truststore alias from the service provider metadata.
ServiceProviderType provider = this.spMetadata.get(serviceName);
if (provider != null && provider.getTruststoreAlias() != null) {
key = this.trustManager.getPublicKey(provider.getTruststoreAlias());
}
// if there was no truststore alias or no PKC under that alias, use the KeyProvider mapping.
if (key == null) {
key = this.trustManager.getValidatingKey(serviceName);
}
} catch (Exception e) {
throw logger.stsPublicKeyError(serviceName, e);
}
}
return key;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getServiceProviderPublicKey(java.lang.String)
*/
public X509Certificate getServiceProviderCertificate(String serviceName) {
X509Certificate x509 = null;
if (this.trustManager != null) {
try {
// try using the truststore alias from the service provider metadata.
ServiceProviderType provider = this.spMetadata.get(serviceName);
if (provider != null && provider.getTruststoreAlias() != null) {
Certificate cer = this.trustManager.getCertificate(provider.getTruststoreAlias());
if (cer instanceof X509Certificate) {
x509 = (X509Certificate) cer;
}
}
// try using the truststore alias from the service provider with regex metadata.
Set<String> keys = this.spRegExMetadata.keySet();
for (String next : keys) {
if (Pattern.matches(next, serviceName)) {
ServiceProviderType providerRegEx = this.spRegExMetadata.get(next);
if (providerRegEx != null && providerRegEx.getTruststoreAlias() != null) {
Certificate cer = this.trustManager.getCertificate(providerRegEx.getTruststoreAlias());
if (cer instanceof X509Certificate) {
x509 = (X509Certificate) cer;
}
}
}
}
// if there was no truststore alias or no PKC under that alias, use the KeyProvider mapping.
if (x509 == null) {
Certificate cer = this.trustManager.getCertificate(serviceName);
if (cer instanceof X509Certificate) {
x509 = (X509Certificate) cer;
}
}
} catch (Exception e) {
throw logger.stsPublicKeyError(serviceName, e);
}
}
return x509;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getSTSKeyPair()
*/
public KeyPair getSTSKeyPair() {
KeyPair keyPair = null;
if (this.trustManager != null) {
try {
keyPair = this.trustManager.getSigningKeyPair();
} catch (Exception e) {
throw logger.stsSigningKeyPairError(e);
}
}
return keyPair;
}
/*
* (non-Javadoc)
*
* @see org.picketlink.identity.federation.core.wstrust.STSConfiguration#getCertificate(java.lang.String)
*/
public Certificate getCertificate(String alias) {
Certificate certificate = null;
if (this.trustManager != null) {
try {
certificate = trustManager.getCertificate(alias);
} catch (Exception e) {
throw logger.stsPublicKeyCertError(e);
}
}
return certificate;
}
/**
* @see STSConfiguration#getXMLDSigCanonicalizationMethod()
*/
public String getXMLDSigCanonicalizationMethod() {
return delegate.getCanonicalizationMethod();
}
/**
* @see {@code STSCoreConfig#addTokenProvider(String, SecurityTokenProvider)}
*/
public void addTokenProvider(String key, SecurityTokenProvider provider) {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(PicketLinkCoreSTS.rte);
tokenProviders.put(key, provider);
QName tokenQName = provider.getSupportedQName();
if (tokenQName != null) {
String tokenElementAndNS = provider.family() + "$" + tokenQName.getLocalPart() + "$" + tokenQName.getNamespaceURI();
this.tokenProviders.put(tokenElementAndNS, provider);
}
}
/**
* @see {@code STSCoreConfig#removeTokenProvider(String)}
*/
public void removeTokenProvider(String key) {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(PicketLinkCoreSTS.rte);
tokenProviders.remove(key);
}
/**
* @see org.picketlink.identity.federation.core.sts.STSCoreConfig#getTokenProviders()
*/
public List<SecurityTokenProvider> getTokenProviders() {
List<SecurityTokenProvider> list = new ArrayList<SecurityTokenProvider>();
list.addAll(tokenProviders.values());
return Collections.unmodifiableList(list);
}
/**
* @see org.picketlink.identity.federation.core.sts.STSCoreConfig#getProvidersByFamily(java.lang.String)
*/
public List<SecurityTokenProvider> getProvidersByFamily(String familyName) {
List<SecurityTokenProvider> result = new ArrayList<SecurityTokenProvider>();
for (SecurityTokenProvider provider : tokenProviders.values()) {
if (provider.family().equals(familyName))
result.add(provider);
}
return result;
}
@Override
public String getSigningCertificateAlias() {
//Check keymanager
if (certificateAlias == null) {
certificateAlias = (String) trustManager.getAdditionalOption(GeneralConstants.X509CERTIFICATE);
}
return certificateAlias;
}
public void setSigningCertificateAlias(String alias) {
this.certificateAlias = alias;
}
/**
* @see org.picketlink.identity.federation.core.sts.STSCoreConfig#copy(org.picketlink.identity.federation.core.sts.STSCoreConfig)
*/
public void copy(STSCoreConfig thatConfig) {
if (thatConfig instanceof PicketLinkSTSConfiguration) {
PicketLinkSTSConfiguration pc = (PicketLinkSTSConfiguration) thatConfig;
this.tokenProviders.putAll(pc.tokenProviders);
this.claimsProcessors.putAll(pc.claimsProcessors);
} else
throw new RuntimeException("Unknown config :" + thatConfig); // TODO: Handle other configuration
}
@Override
public String toString() {
return "PicketLinkSTSConfiguration [delegate=" + delegate + ", tokenProviders=" + tokenProviders + ", spMetadata="
+ spMetadata + ", claimsProcessors=" + claimsProcessors + ", trustManager=" + trustManager + ", handler="
+ handler + "]";
}
}