/* * 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.sts; import org.picketlink.common.ErrorCodes; import org.picketlink.common.PicketLinkLogger; import org.picketlink.common.PicketLinkLoggerFactory; import org.picketlink.common.exceptions.ConfigurationException; import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.config.federation.STSType; import org.picketlink.config.federation.parsers.STSConfigParser; import org.picketlink.identity.federation.core.interfaces.ProtocolContext; import org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider; import org.picketlink.identity.federation.core.wstrust.PicketLinkSTSConfiguration; import org.picketlink.identity.federation.core.wstrust.STSConfiguration; import javax.xml.namespace.QName; import java.io.File; import java.io.InputStream; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; /** * <p> * Generic STS Core. * </p> * <p> * This is a Singleton Class. * </p> * * @author Anil.Saldhana@redhat.com * @see {@code #instance()} * @since Dec 27, 2010 */ public class PicketLinkCoreSTS { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); public static final RuntimePermission rte = new RuntimePermission("org.picketlink.sts"); protected STSCoreConfig configuration; private static PicketLinkCoreSTS _instance = null; private static final String SEPARATOR = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("file.separator"); } }); private static final String STS_CONFIG_DIR = "picketlink-store" + SEPARATOR + "sts" + SEPARATOR; protected PicketLinkCoreSTS() { } public static PicketLinkCoreSTS instance() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(rte); } if (_instance == null) _instance = new PicketLinkCoreSTS(); return _instance; } public void initialize(STSCoreConfig config) { if (this.configuration != null) { this.configuration.copy(config); } else this.configuration = config; } public void installDefaultConfiguration(String... configFileName) { String fileName = "core-sts.xml"; if (configFileName != null && configFileName.length > 0) fileName = configFileName[0]; if (configuration == null) { logger.trace("[InstallDefaultConfiguration] Configuration is null. Creating a new configuration"); configuration = new PicketLinkSTSConfiguration(); } try { logger.trace("[InstallDefaultConfiguration] Configuration file name=" + fileName); STSConfiguration config = getConfiguration(fileName); configuration.copy(config); } catch (ConfigurationException e) { throw new RuntimeException(e); } } /** * Issue a security token * * @param protocolContext * * @throws ProcessingException * @throws {@link SecurityException} if the caller does not have a runtime permission for "org.picketlink.sts" */ public void issueToken(ProtocolContext protocolContext) throws ProcessingException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(rte); SecurityTokenProvider provider = getProvider(protocolContext); if (provider == null) throw logger.stsNoTokenProviderError(configuration.toString(), protocolContext.toString()); logger.debug("issueToken::provider=" + provider); provider.issueToken(protocolContext); } /** * <p> * Renews the security token contained in the specified request context. This method is used when a previously * generated * token has expired, generating a new version of the same token with different expiration semantics. * </p> * * @param protocolContext the {@code ProtocolContext} that contains the token to be renewed. * * @throws ProcessingException if an error occurs while renewing the security token. * @throws {@link SecurityException} if the caller does not have a runtime permission for "org.picketlink.sts" */ public void renewToken(ProtocolContext protocolContext) throws ProcessingException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(rte); SecurityTokenProvider provider = null; if (provider == null) provider = getProviderBasedOnQName(protocolContext); if (provider == null) throw logger.stsNoTokenProviderError(configuration.toString(), protocolContext.toString()); logger.debug("renewToken::provider=" + provider); provider.renewToken(protocolContext); } /** * <p> * Cancels the token contained in the specified request context. A security token is usually canceled when one wants * to make * sure that the token will not be used anymore. A security token can't be renewed once it has been canceled. * </p> * * @param protocolContext the {@code ProtocolContext} that contains the token to be canceled. * * @throws ProcessingException if an error occurs while canceling the security token. * @throws {@link SecurityException} if the caller does not have a runtime permission for "org.picketlink.sts" */ public void cancelToken(ProtocolContext protocolContext) throws ProcessingException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(rte); SecurityTokenProvider provider = null; if (provider == null) provider = getProviderBasedOnQName(protocolContext); if (provider == null) throw logger.stsNoTokenProviderError("", protocolContext.toString()); logger.debug("cancelToken::provider=" + provider); provider.cancelToken(protocolContext); } /** * <p> * Evaluates the validity of the token contained in the specified request context and sets the result in the context * itself. * The result can be a status, a new token, or both. * </p> * * @param protocolContext the {@code ProtocolContext} that contains the token to be validated. * * @throws ProcessingException if an error occurs while validating the security token. * @throws {@link SecurityException} if the caller does not have a runtime permission for "org.picketlink.sts" */ public void validateToken(ProtocolContext protocolContext) throws ProcessingException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(rte); SecurityTokenProvider provider = null; if (provider == null) provider = getProviderBasedOnQName(protocolContext); if (provider == null) throw logger.stsNoTokenProviderError(configuration.toString(), protocolContext.toString()); logger.debug("validateToken::provider=" + provider); provider.validateToken(protocolContext); } private SecurityTokenProvider getProvider(ProtocolContext protocolContext) { if (configuration == null) throw new RuntimeException(ErrorCodes.STS_CONFIGURATION_NOT_SET); SecurityTokenProvider provider = null; // Special Case: WST Applies To String serviceName = protocolContext.serviceName(); if (serviceName != null) { provider = this.configuration.getProviderForService(serviceName); } if (provider == null) { // lets get the provider based on token type String tokenType = protocolContext.tokenType(); if (tokenType != null) provider = this.configuration.getProviderForTokenType(protocolContext.tokenType()); } return provider; } private SecurityTokenProvider getProviderBasedOnQName(ProtocolContext protocolContext) throws ProcessingException { SecurityTokenProvider provider = null; QName qname = null; if (provider == null) { qname = protocolContext.getQName(); if (qname == null) throw new ProcessingException(ErrorCodes.NULL_VALUE + "QName of the token type"); provider = this.configuration.getProviderForTokenElementNS(protocolContext.family(), qname); } if (provider == null) throw new ProcessingException(ErrorCodes.STS_NO_TOKEN_PROVIDER + qname.getNamespaceURI() + ":" + qname.getLocalPart()); return provider; } /** * <p> * Obtains the STS configuration options. * </p> * * @return an instance of {@code STSConfiguration} containing the STS configuration properties. */ protected STSConfiguration getConfiguration(String fileName) throws ConfigurationException { URL configurationFileURL = null; try { // check the user home for a configuration file generated by the picketlink console. String configurationFilePath = System.getProperty("user.home") + SEPARATOR + STS_CONFIG_DIR + fileName; File configurationFile = new File(configurationFilePath); if (configurationFile.exists()) configurationFileURL = configurationFile.toURI().toURL(); else { // if not configuration file was found in the user home, check the context classloader. configurationFileURL = SecurityActions.loadResource(getClass(), fileName); } // if no configuration file was found, log a warn message and use default configuration values. if (configurationFileURL == null) { logger.stsConfigurationFileNotFoundTCL(fileName); ClassLoader clazzLoader = SecurityActions.getClassLoader(getClass()); configurationFileURL = clazzLoader.getResource(fileName); } // if no configuration file was found, log a warn message and use default configuration values. if (configurationFileURL == null) { logger.stsConfigurationFileNotFoundClassLoader(fileName); try { configurationFileURL = new URL(fileName); } catch (Exception e) { return new PicketLinkSTSConfiguration(); } finally { if (configurationFileURL == null) { logger.stsUsingDefaultConfiguration(fileName); return new PicketLinkSTSConfiguration(); } } } InputStream stream = configurationFileURL.openStream(); STSType stsConfig = (STSType) new STSConfigParser().parse(stream); STSConfiguration configuration = new PicketLinkSTSConfiguration(stsConfig); logger.stsConfigurationFileLoaded(fileName); return configuration; } catch (Exception e) { throw logger.stsConfigurationFileParsingError(e); } } public STSCoreConfig getConfiguration() { return this.configuration; } }