/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.picketlink.identity.federation.bindings.tomcat.idp;
import org.apache.catalina.*;
import org.apache.catalina.authenticator.SSLAuthenticator;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.valves.ValveBase;
import org.apache.coyote.ActionCode;
import org.jboss.security.audit.AuditLevel;
import org.picketlink.identity.federation.PicketLinkLogger;
import org.picketlink.identity.federation.PicketLinkLoggerFactory;
import org.picketlink.identity.federation.bindings.tomcat.TomcatRoleGenerator;
import org.picketlink.identity.federation.core.audit.PicketLinkAuditEvent;
import org.picketlink.identity.federation.core.audit.PicketLinkAuditEventType;
import org.picketlink.identity.federation.core.audit.PicketLinkAuditHelper;
import org.picketlink.identity.federation.core.config.AuthPropertyType;
import org.picketlink.identity.federation.core.config.IDPType;
import org.picketlink.identity.federation.core.config.KeyProviderType;
import org.picketlink.identity.federation.core.config.PicketLinkType;
import org.picketlink.identity.federation.core.exceptions.ConfigurationException;
import org.picketlink.identity.federation.core.exceptions.ParsingException;
import org.picketlink.identity.federation.core.exceptions.ProcessingException;
import org.picketlink.identity.federation.core.handler.config.Handlers;
import org.picketlink.identity.federation.core.impl.DelegatedAttributeManager;
import org.picketlink.identity.federation.core.interfaces.AttributeManager;
import org.picketlink.identity.federation.core.interfaces.ProtocolContext;
import org.picketlink.identity.federation.core.interfaces.RoleGenerator;
import org.picketlink.identity.federation.core.interfaces.TrustKeyManager;
import org.picketlink.identity.federation.core.saml.v1.SAML11Constants;
import org.picketlink.identity.federation.core.saml.v1.SAML11ProtocolContext;
import org.picketlink.identity.federation.core.saml.v1.writers.SAML11ResponseWriter;
import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
import org.picketlink.identity.federation.core.saml.v2.constants.JBossSAMLURIConstants;
import org.picketlink.identity.federation.core.saml.v2.exceptions.IssuerNotTrustedException;
import org.picketlink.identity.federation.core.saml.v2.factories.SAML2HandlerChainFactory;
import org.picketlink.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerChainConfig;
import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerRequest;
import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerResponse;
import org.picketlink.identity.federation.core.saml.v2.interfaces.*;
import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler.HANDLER_TYPE;
import org.picketlink.identity.federation.core.saml.v2.util.AssertionUtil;
import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil;
import org.picketlink.identity.federation.core.saml.v2.util.HandlerUtil;
import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS;
import org.picketlink.identity.federation.core.util.*;
import org.picketlink.identity.federation.core.wstrust.PicketLinkSTSConfiguration;
import org.picketlink.identity.federation.saml.v1.assertion.*;
import org.picketlink.identity.federation.saml.v1.assertion.SAML11SubjectType.SAML11SubjectTypeChoice;
import org.picketlink.identity.federation.saml.v1.protocol.SAML11ResponseType;
import org.picketlink.identity.federation.saml.v1.protocol.SAML11StatusType;
import org.picketlink.identity.federation.saml.v2.SAML2Object;
import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.SPSSODescriptorType;
import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
import org.picketlink.identity.federation.saml.v2.protocol.RequestAbstractType;
import org.picketlink.identity.federation.saml.v2.protocol.StatusResponseType;
import org.picketlink.identity.federation.web.config.AbstractSAMLConfigurationProvider;
import org.picketlink.identity.federation.web.constants.GeneralConstants;
import org.picketlink.identity.federation.web.core.HTTPContext;
import org.picketlink.identity.federation.web.core.IdentityParticipantStack;
import org.picketlink.identity.federation.web.core.IdentityServer;
import org.picketlink.identity.federation.web.util.ConfigurationUtil;
import org.picketlink.identity.federation.web.util.IDPWebRequestUtil;
import org.picketlink.identity.federation.web.util.IDPWebRequestUtil.WebRequestUtilHolder;
import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
import org.picketlink.identity.federation.web.util.SAMLConfigurationProvider;
import org.w3c.dom.Document;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static org.picketlink.identity.federation.core.util.StringUtil.isNotNull;
import static org.picketlink.identity.federation.core.util.StringUtil.isNullOrEmpty;
/**
* Base Class for the IDPWebBrowserSSOValve
*
* @author anil saldhana
*/
public abstract class AbstractIDPValve extends ValveBase {
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
protected boolean enableAudit = false;
protected PicketLinkAuditHelper auditHelper = null;
protected IDPType idpConfiguration = null;
protected PicketLinkType picketLinkConfiguration = null;
private RoleGenerator roleGenerator = new TomcatRoleGenerator();
private TrustKeyManager keyManager;
private transient DelegatedAttributeManager attribManager = new DelegatedAttributeManager();
private final List<String> attributeKeys = new ArrayList<String>();
private transient SAML2HandlerChain chain = null;
/**
* The user can inject a fully qualified name of a {@link SAMLConfigurationProvider}
*/
protected SAMLConfigurationProvider configProvider = null;
/**
* A Lock for Handler operations in the chain
*/
private final Lock chainLock = new ReentrantLock();
private Map<String, SPSSODescriptorType> spSSOMetadataMap = new HashMap<String, SPSSODescriptorType>();
private SSLAuthenticator sslAuthenticator;
private Boolean passUserPrincipalToAttributeManager = false;
/**
* Character encoding to use when reading the request parameters
*/
protected String characterEncoding = null;
/**
* Return the character encoding to use when reading the request parameters
*/
public String getCharacterEncoding() {
return characterEncoding;
}
/**
* Set the character encoding to use when reading the request parameters
*/
public void setCharacterEncoding(String encoding) {
characterEncoding = encoding;
}
// Set a list of attributes we are interested in separated by comma
public void setAttributeList(String attribList) {
if (StringUtil.isNotNull(attribList)) {
this.attributeKeys.clear();
this.attributeKeys.addAll(StringUtil.tokenize(attribList));
}
}
/**
* Set the {@link SAMLConfigurationProvider} fqn
*
* @param cp fqn of a {@link SAMLConfigurationProvider}
*/
public void setConfigProvider(String cp) {
if (cp == null)
throw logger.nullArgumentError("configProvider");
Class<?> clazz = SecurityActions.loadClass(getClass(), cp);
if (clazz == null)
throw new RuntimeException(logger.classNotLoadedError(cp));
try {
configProvider = (SAMLConfigurationProvider) clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(logger.couldNotCreateInstance(cp, e));
}
}
public void setConfigProvider(SAMLConfigurationProvider configurationProvider) {
this.configProvider = configurationProvider;
}
@Deprecated
public void setRoleGenerator(String rgName) {
logger.warn("Option 'roleGenerator' is deprecated and should not be used. This configuration is now set in picketlink.xml.");
}
@Deprecated
public void setSamlHandlerChainClass(String samlHandlerChainClass) {
logger.warn("Option 'samlHandlerChainClass' is deprecated and should not be used. This configuration is now set in picketlink.xml.");
}
@Deprecated
public void setIdentityParticipantStack(String fqn) {
logger.warn("Option 'identityParticipantStack' is deprecated and should not be used. This configuration is now set in picketlink.xml.");
}
@Deprecated
public void setStrictPostBinding(Boolean strictPostBinding) {
logger.warn("Option 'strictPostBinding' is deprecated and should not be used. This configuration is now set in picketlink.xml.");
}
@Deprecated
public Boolean getIgnoreIncomingSignatures() {
logger.warn("Option 'ignoreIncomingSignatures' is deprecated and should not be used. Signatures are verified if "
+ "SAML2SignatureValidationHandler is available.");
return false;
}
@Deprecated
public void setIgnoreIncomingSignatures(Boolean ignoreIncomingSignature) {
logger.warn("Option 'ignoreIncomingSignatures' is deprecated and not used. Signatures are verified if "
+ "SAML2SignatureValidationHandler is available.");
}
/**
* PLFED-248 Allows to validate the token's signature against the keystore using the token's issuer.
*/
@Deprecated
public void setValidatingAliasToTokenIssuer(Boolean validatingAliasToTokenIssuer) {
logger.warn("Option 'validatingAliasToTokenIssuer' is deprecated and not used. The IDP will always use the issuer host to validate signatures.");
}
/**
* IDP should not do any attributes such as generation of roles etc
*
* @param ignoreAttributes
*/
public void setIgnoreAttributesGeneration(Boolean ignoreAttributes) {
if (ignoreAttributes == Boolean.TRUE)
this.attribManager = null;
}
@Deprecated
public Boolean getSignOutgoingMessages() {
logger.warn("Option signOutgoingMessages is used for signing of error messages. Normal SAML messages are "
+ "signed by SAML2SignatureGenerationHandler.");
return true;
}
@Deprecated
public void setSignOutgoingMessages(Boolean signOutgoingMessages) {
logger.warn("Option signOutgoingMessages is used for signing of error messages. Normal SAML messages are "
+ "signed by SAML2SignatureGenerationHandler.");
}
/**
* IDP should get the user principal from Request.getUserPrincipal() and send that to the attribute manager
*
* @param passUserPrincipalToAttributeManager
*/
public void setPassUserPrincipalToAttributeManager(Boolean passUserPrincipalToAttributeManager) {
this.passUserPrincipalToAttributeManager = passUserPrincipalToAttributeManager;
}
/**
* <p>
* Returns the configurations used.
* </p>
*
* @return
*/
public PicketLinkType getConfiguration() {
return this.picketLinkConfiguration;
}
/**
* Return the {@link TrustKeyManager}
*
* @return
*/
public TrustKeyManager getKeyManager() {
return this.keyManager;
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String characterEncoding = getCharacterEncoding();
if (characterEncoding != null) {
request.setCharacterEncoding(characterEncoding);
}
// Look for unauthorized status
if (isUnauthorized(response)) {
handleUnauthorizedResponse(request, response);
return;
}
// first, we populate all required parameters sent into session for later retrieval. If they exists.
populateSessionWithSAMLParameters(request);
// get an authenticated user or tries to authenticate if this is a authentication request
Principal userPrincipal = getUserPrincipal(request, response);
// we only handle SAML messages for authenticated users.
if (userPrincipal != null) {
handleSAMLMessage(request, response);
}
}
/**
* <p>
* Handles SAML messages.
* </p>
*
* @param request
* @param response
* @throws IOException
* @throws ServletException
*/
private void handleSAMLMessage(Request request, Response response) throws IOException, ServletException {
if (hasSAML11Target(request)) {
// We have SAML 1.1 IDP first scenario. Now we need to create a SAMLResponse and send back
// to SP as per target
handleSAML11(request, response);
} else {
Session session = request.getSessionInternal();
String samlRequestMessage = (String) session.getNote(GeneralConstants.SAML_REQUEST_KEY);
String samlResponseMessage = (String) session.getNote(GeneralConstants.SAML_RESPONSE_KEY);
/**
* Since the container has finished the authentication, we can retrieve the original saml message as well as any
* relay state from the SP
*/
String relayState = (String) session.getNote(GeneralConstants.RELAY_STATE);
String signature = (String) session.getNote(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
String sigAlg = (String) session.getNote(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
if (logger.isTraceEnabled()) {
StringBuilder builder = new StringBuilder();
builder.append("Retrieved saml messages and relay state from session");
builder.append("saml Request message=").append(samlRequestMessage);
builder.append("::").append("SAMLResponseMessage=");
builder.append(samlResponseMessage).append(":").append("relay state=").append(relayState);
builder.append("Signature=").append(signature).append("::sigAlg=").append(sigAlg);
logger.trace(builder.toString());
}
if (isNotNull(samlRequestMessage)) {
processSAMLRequestMessage(request, response);
} else if (isNotNull(samlResponseMessage)) {
processSAMLResponseMessage(request, response);
} else if (request.getRequestURI().equals(request.getContextPath() + "/")) {
// no SAML processing and the request is asking for /.
forwardHosted(request, response);
}
}
}
/**
* <p>
* Checks if the given {@link Request} containes a SAML11 Target parameter. Usually this indicates that the given request is
* a SAML11 request.
* </p>
*
* @param request
* @return
*/
private boolean hasSAML11Target(Request request) {
return isNotNull(request.getParameter(SAML11Constants.TARGET));
}
private void forwardHosted(Request request, Response response) throws ServletException, IOException {
logger.trace("SAML 1.1::Proceeding to IDP index page");
RequestDispatcher dispatch = getContext().getServletContext()
.getRequestDispatcher(this.idpConfiguration.getHostedURI());
recycle(response);
try {
includeResource(request, response, dispatch);
} catch (ClassCastException cce) {
// JBAS5.1 and 6 quirkiness
includeResource(request.getRequest(), response, dispatch);
}
}
/**
* <p>
* Before forwarding we need to know the content length of the target resource in order to configure the response properly.
* This is necessary because the valve already have written to the response, and we want to override with the target
* resource data.
* </p>
*
* @param request
* @param response
* @param dispatch
* @throws ServletException
* @throws IOException
*/
private void includeResource(ServletRequest request, Response response, RequestDispatcher dispatch)
throws ServletException, IOException {
dispatch.include(request, response);
// we need to re-configure the content length because Tomcat will truncate the output with the size of the welcome page
// (eg.: index.html).
response.getCoyoteResponse().setContentLength(response.getContentCount());
}
/**
* <p>
* SAML parameters are also populated into session if they are present in the request. This allows the IDP to retrieve them
* later when handling a specific SAML request or response.
* </p>
*
* @param request
* @return
* @throws IOException
*/
private void populateSessionWithSAMLParameters(Request request) throws IOException {
String samlRequestMessage = request.getParameter(GeneralConstants.SAML_REQUEST_KEY);
String samlResponseMessage = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
boolean containsSAMLRequestMessage = isNotNull(samlRequestMessage);
boolean containsSAMLResponseMessage = isNotNull(samlResponseMessage);
String signature = request.getParameter(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
String sigAlg = request.getParameter(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
String relayState = request.getParameter(GeneralConstants.RELAY_STATE);
Session session = request.getSessionInternal();
if (containsSAMLRequestMessage || containsSAMLResponseMessage) {
logger.trace("Storing the SAMLRequest/SAMLResponse and RelayState in session");
if (isNotNull(samlRequestMessage))
session.setNote(GeneralConstants.SAML_REQUEST_KEY, samlRequestMessage);
if (isNotNull(samlResponseMessage))
session.setNote(GeneralConstants.SAML_RESPONSE_KEY, samlResponseMessage);
if (isNotNull(relayState))
session.setNote(GeneralConstants.RELAY_STATE, relayState.trim());
if (isNotNull(signature))
session.setNote(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY, signature.trim());
if (isNotNull(sigAlg))
session.setNote(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, sigAlg.trim());
}
}
/**
* <p>
* Handles an unauthorized response returned by a service provider.
* </p>
*
* @param request
* @param response
* @throws IOException
* @throws ServletException
*/
private void handleUnauthorizedResponse(Request request, Response response) throws IOException, ServletException {
IDPWebRequestUtil webRequestUtil = new IDPWebRequestUtil(request, idpConfiguration, keyManager);
Document samlErrorResponse = null;
String referer = request.getHeader("Referer");
String relayState = request.getParameter(GeneralConstants.RELAY_STATE);
try {
samlErrorResponse = webRequestUtil.getErrorResponse(referer, JBossSAMLURIConstants.STATUS_AUTHNFAILED.get(),
getIdentityURL(), this.idpConfiguration.isSupportsSignature());
WebRequestUtilHolder holder = webRequestUtil.getHolder();
holder.setResponseDoc(samlErrorResponse).setDestination(referer).setRelayState(relayState)
.setAreWeSendingRequest(false).setPrivateKey(null).setSupportSignature(false).setServletResponse(response)
.setErrorResponse(true);
holder.setPostBindingRequested(webRequestUtil.hasSAMLRequestInPostProfile());
if (this.idpConfiguration.isSupportsSignature()) {
holder.setSupportSignature(true).setPrivateKey(keyManager.getSigningKey());
}
holder.setStrictPostBinding(this.idpConfiguration.isStrictPostBinding());
webRequestUtil.send(holder);
} catch (GeneralSecurityException e) {
throw new ServletException(e);
}
}
private boolean isUnauthorized(Response response) {
return response.getStatus() == HttpServletResponse.SC_FORBIDDEN;
}
/**
* <p>
* Returns the authenticated principal. If there is no principal associated with the {@link Request}, null is returned.
* </p>
*
* @param request
* @param response
* @return
* @throws IOException
* @throws ServletException
*/
private Principal getUserPrincipal(Request request, Response response) throws IOException, ServletException {
Principal userPrincipal = request.getPrincipal();
if (userPrincipal == null) {
if (this.idpConfiguration.isSSLClientAuthentication()) {
if (request.isSecure()) {
getSSLAuthenticator().invoke(request, response);
// we always reset/recycle the response to remove any data written to the response by the ssl
// authenticator
response.resetBuffer();
response.recycle();
}
}
userPrincipal = request.getPrincipal();
// we always fall back to the configured authentication method.
if (userPrincipal == null) {
getNext().invoke(request, response);
}
userPrincipal = request.getPrincipal();
}
return userPrincipal;
}
public Principal authenticateSSL(Request request, Response response) throws IOException {
// Retrieve the certificate chain for this client
if (containerLog.isDebugEnabled())
containerLog.debug(" Looking up certificates");
X509Certificate certs[] = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
if ((certs == null) || (certs.length < 1)) {
try {
request.getCoyoteRequest().action(ActionCode.ACTION_REQ_SSL_CERTIFICATE, null);
} catch (IllegalStateException ise) {
// Request body was too large for save buffer
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, sm.getString("authenticator.certificates"));
return null;
}
certs = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
}
if ((certs == null) || (certs.length < 1)) {
if (containerLog.isDebugEnabled())
containerLog.debug(" No certificates included with this request");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, sm.getString("authenticator.certificates"));
return null;
}
// Authenticate the specified certificate chain
Principal principal = getContext().getRealm().authenticate(certs);
if (principal == null) {
if (containerLog.isDebugEnabled())
containerLog.debug(" Realm.authenticate() returned false");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, sm.getString("authenticator.unauthorized"));
return null;
}
return principal;
}
protected void handleSAML11(Request request, Response response) throws ServletException, IOException {
try {
IDPWebRequestUtil webRequestUtil = new IDPWebRequestUtil(request, idpConfiguration, keyManager);
Principal userPrincipal = request.getPrincipal();
String contextPath = getContextPath();
String target = request.getParameter(SAML11Constants.TARGET);
Session session = request.getSessionInternal();
SAML11AssertionType saml11Assertion = (SAML11AssertionType) session.getNote("SAML11");
if (saml11Assertion == null) {
SAML11ProtocolContext saml11Protocol = new SAML11ProtocolContext();
saml11Protocol.setIssuerID(getIdentityURL());
SAML11SubjectType subject = new SAML11SubjectType();
SAML11SubjectTypeChoice subjectChoice = new SAML11SubjectTypeChoice(new SAML11NameIdentifierType(
userPrincipal.getName()));
subject.setChoice(subjectChoice);
saml11Protocol.setSubjectType(subject);
PicketLinkCoreSTS.instance().issueToken(saml11Protocol);
saml11Assertion = saml11Protocol.getIssuedAssertion();
session.setNote("SAML11", saml11Assertion);
if (AssertionUtil.hasExpired(saml11Assertion)) {
saml11Protocol.setIssuedAssertion(saml11Assertion);
PicketLinkCoreSTS.instance().renewToken(saml11Protocol);
saml11Assertion = saml11Protocol.getIssuedAssertion();
session.setNote("SAML11", saml11Assertion);
}
}
GenericPrincipal genericPrincipal = (GenericPrincipal) userPrincipal;
String[] roles = genericPrincipal.getRoles();
SAML11AttributeStatementType attributeStatement = this.createAttributeStatement(Arrays.asList(roles));
if (attributeStatement != null) {
saml11Assertion.add(attributeStatement);
}
// Send it as SAMLResponse
String id = IDGenerator.create("ID_");
SAML11ResponseType saml11Response = new SAML11ResponseType(id, XMLTimeUtil.getIssueInstant());
saml11Response.add(saml11Assertion);
saml11Response.setStatus(SAML11StatusType.successType());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
SAML11ResponseWriter writer = new SAML11ResponseWriter(StaxUtil.getXMLStreamWriter(baos));
writer.write(saml11Response);
Document samlResponse = DocumentUtil.getDocument(new ByteArrayInputStream(baos.toByteArray()));
WebRequestUtilHolder holder = webRequestUtil.getHolder();
holder.setResponseDoc(samlResponse).setDestination(target).setRelayState("").setAreWeSendingRequest(false)
.setPrivateKey(null).setSupportSignature(false).setServletResponse(response);
if (enableAudit) {
PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
auditEvent.setType(PicketLinkAuditEventType.RESPONSE_TO_SP);
auditEvent.setDestination(target);
auditEvent.setWhoIsAuditing(contextPath);
auditHelper.audit(auditEvent);
}
response.getCoyoteResponse().recycle();
webRequestUtil.send(holder);
} catch (GeneralSecurityException e) {
logger.samlIDPHandlingSAML11Error(e);
throw new ServletException();
}
}
protected void processSAMLRequestMessage(Request request, Response response) throws IOException {
Principal userPrincipal = request.getPrincipal();
Session session = request.getSessionInternal();
SAMLDocumentHolder samlDocumentHolder = null;
SAML2Object samlObject = null;
Document samlResponse = null;
boolean isErrorResponse = false;
String destination = null;
String destinationQueryStringWithSignature = null;
Boolean requestedPostProfile = null;
String samlRequestMessage = (String) session.getNote(GeneralConstants.SAML_REQUEST_KEY);
String relayState = (String) session.getNote(GeneralConstants.RELAY_STATE);
String contextPath = getContextPath();
boolean willSendRequest = false;
String referer = request.getHeader("Referer");
cleanUpSessionNote(request);
// Determine the transport mechanism
boolean isSecure = request.isSecure();
String loginType = determineLoginType(isSecure);
IDPWebRequestUtil webRequestUtil = new IDPWebRequestUtil(request, idpConfiguration, keyManager);
try {
samlDocumentHolder = webRequestUtil.getSAMLDocumentHolder(samlRequestMessage);
samlObject = samlDocumentHolder.getSamlObject();
if (!(samlObject instanceof RequestAbstractType)) {
throw logger.wrongTypeError(samlObject.getClass().getName());
}
// Get the SAML Request Message
RequestAbstractType requestAbstractType = (RequestAbstractType) samlObject;
String issuer = requestAbstractType.getIssuer().getValue();
if (samlRequestMessage == null)
throw logger.samlIDPValidationCheckFailed();
IssuerInfoHolder idpIssuer = new IssuerInfoHolder(getIdentityURL());
ProtocolContext protocolContext = new HTTPContext(request, response, getContext().getServletContext());
// Create the request/response
SAML2HandlerRequest saml2HandlerRequest = new DefaultSAML2HandlerRequest(protocolContext, idpIssuer.getIssuer(),
samlDocumentHolder, HANDLER_TYPE.IDP);
saml2HandlerRequest.setRelayState(relayState);
if (StringUtil.isNotNull(loginType)) {
saml2HandlerRequest.addOption(GeneralConstants.LOGIN_TYPE, loginType);
}
String assertionID = (String) session.getSession().getAttribute(GeneralConstants.ASSERTION_ID);
// Set the options on the handler request
Map<String, Object> requestOptions = new HashMap<String, Object>();
requestOptions.put(GeneralConstants.IGNORE_SIGNATURES, willIgnoreSignatureOfCurrentRequest(issuer));
requestOptions.put(GeneralConstants.SP_SSO_METADATA_DESCRIPTOR, spSSOMetadataMap.get(issuer));
requestOptions.put(GeneralConstants.ROLE_GENERATOR, roleGenerator);
requestOptions.put(GeneralConstants.CONFIGURATION, this.idpConfiguration);
requestOptions.put(GeneralConstants.SAML_IDP_STRICT_POST_BINDING, this.idpConfiguration.isStrictPostBinding());
requestOptions.put(GeneralConstants.SUPPORTS_SIGNATURES, this.idpConfiguration.isSupportsSignature());
if (assertionID != null)
requestOptions.put(GeneralConstants.ASSERTION_ID, assertionID);
if (this.keyManager != null) {
PublicKey validatingKey = getIssuerPublicKey(request, issuer);
requestOptions.put(GeneralConstants.SENDER_PUBLIC_KEY, validatingKey);
requestOptions.put(GeneralConstants.DECRYPTING_KEY, keyManager.getSigningKey());
}
// if this is a SAML AuthnRequest load the roles using the generator.
if (requestAbstractType instanceof AuthnRequestType) {
List<String> roles = roleGenerator.generateRoles(userPrincipal);
session.getSession().setAttribute(GeneralConstants.ROLES_ID, roles);
Map<String, Object> attribs = this.attribManager.getAttributes(
passUserPrincipalToAttributeManager == true
? request.getUserPrincipal()
: userPrincipal,
attributeKeys);
requestOptions.put(GeneralConstants.ATTRIBUTES, attribs);
}
if (auditHelper != null) {
requestOptions.put(GeneralConstants.AUDIT_HELPER, auditHelper);
requestOptions.put(GeneralConstants.CONTEXT_PATH, contextPath);
}
saml2HandlerRequest.setOptions(requestOptions);
SAML2HandlerResponse saml2HandlerResponse = new DefaultSAML2HandlerResponse();
Set<SAML2Handler> handlers = chain.handlers();
logger.trace("Handlers are=" + handlers);
// the trusted domains is done by a handler
// webRequestUtil.isTrusted(issuer);
if (handlers != null) {
try {
chainLock.lock();
for (SAML2Handler handler : handlers) {
handler.handleRequestType(saml2HandlerRequest, saml2HandlerResponse);
willSendRequest = saml2HandlerResponse.getSendRequest();
}
} finally {
chainLock.unlock();
}
}
samlResponse = saml2HandlerResponse.getResultingDocument();
relayState = saml2HandlerResponse.getRelayState();
destination = saml2HandlerResponse.getDestination();
requestedPostProfile = saml2HandlerResponse.isPostBindingForResponse();
destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
} catch (Exception e) {
String status = JBossSAMLURIConstants.STATUS_AUTHNFAILED.get();
if (e instanceof IssuerNotTrustedException || e.getCause() instanceof IssuerNotTrustedException) {
status = JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get();
}
logger.samlIDPRequestProcessingError(e);
samlResponse = webRequestUtil.getErrorResponse(referer, status, getIdentityURL(),
this.idpConfiguration.isSupportsSignature());
isErrorResponse = true;
} finally {
try {
// if the destination is null, probably because some error occur during authentication, use the AuthnRequest
// AssertionConsumerServiceURL as the destination
if (destination == null && samlObject instanceof AuthnRequestType) {
AuthnRequestType authRequest = (AuthnRequestType) samlObject;
destination = authRequest.getAssertionConsumerServiceURL().toASCIIString();
}
// if destination is still empty redirect the user to the identity url. If the user is already authenticated he
// will be probably redirected to the idp hosted page.
if (destination == null) {
response.sendRedirect(getIdentityURL());
} else {
WebRequestUtilHolder holder = webRequestUtil.getHolder();
holder.setResponseDoc(samlResponse).setDestination(destination).setRelayState(relayState)
.setAreWeSendingRequest(willSendRequest).setPrivateKey(null).setSupportSignature(false)
.setErrorResponse(isErrorResponse).setServletResponse(response)
.setDestinationQueryStringWithSignature(destinationQueryStringWithSignature);
holder.setStrictPostBinding(this.idpConfiguration.isStrictPostBinding());
if (requestedPostProfile != null)
holder.setPostBindingRequested(requestedPostProfile);
else
holder.setPostBindingRequested(webRequestUtil.hasSAMLRequestInPostProfile());
if (this.idpConfiguration.isSupportsSignature()) {
holder.setPrivateKey(keyManager.getSigningKey()).setSupportSignature(true);
}
if (holder.isPostBinding())
recycle(response);
if (enableAudit) {
PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
auditEvent.setType(PicketLinkAuditEventType.RESPONSE_TO_SP);
auditEvent.setDestination(destination);
auditEvent.setWhoIsAuditing(contextPath);
auditHelper.audit(auditEvent);
}
webRequestUtil.send(holder);
}
} catch (ParsingException e) {
logger.samlAssertionPasingFailed(e);
} catch (GeneralSecurityException e) {
logger.trace("Security Exception:", e);
} catch (Exception e) {
System.out.println(e);
}
}
return;
}
/**
* Returns the PublicKey to be used for the token's signature verification. This key is related with the issuer of the SAML
* message received by the IDP.
*
* @param request
* @param issuer
* @return
* @throws ProcessingException
* @throws ConfigurationException
*/
private PublicKey getIssuerPublicKey(Request request, String issuer) throws ConfigurationException, ProcessingException {
String issuerHost = null;
PublicKey issuerPublicKey = null;
try {
issuerHost = new URL(issuer).getHost();
} catch (MalformedURLException e) {
logger.trace("Token issuer is not a valid URL: " + issuer, e);
issuerHost = issuer;
}
logger.trace("Trying to find a PK for issuer: " + issuerHost);
try {
issuerPublicKey = CoreConfigUtil.getValidatingKey(keyManager, issuerHost);
} catch (IllegalStateException ise) {
logger.trace("Token issuer is not found for: " + issuer, ise);
}
if (issuerPublicKey == null) {
issuerHost = request.getRemoteAddr();
logger.trace("Trying to find a PK for issuer " + issuerHost);
issuerPublicKey = CoreConfigUtil.getValidatingKey(keyManager, issuerHost);
}
logger.trace("Using Validating Alias=" + issuerHost + " to check signatures.");
return issuerPublicKey;
}
protected void processSAMLResponseMessage(Request request, Response response) throws ServletException, IOException {
Session session = request.getSessionInternal();
SAMLDocumentHolder samlDocumentHolder = null;
SAML2Object samlObject = null;
Document samlResponse = null;
boolean isErrorResponse = false;
String destination = null;
String destinationQueryStringWithSignature = null;
String contextPath = getContextPath();
boolean requestedPostProfile = false;
// Get the SAML Response Message
String samlResponseMessage = (String) session.getNote(GeneralConstants.SAML_RESPONSE_KEY);
String relayState = (String) session.getNote(GeneralConstants.RELAY_STATE);
boolean willSendRequest = false;
String referer = request.getHeader("Referer");
cleanUpSessionNote(request);
IDPWebRequestUtil webRequestUtil = new IDPWebRequestUtil(request, idpConfiguration, keyManager);
try {
samlDocumentHolder = webRequestUtil.getSAMLDocumentHolder(samlResponseMessage);
samlObject = samlDocumentHolder.getSamlObject();
if (!(samlObject instanceof StatusResponseType)) {
throw logger.wrongTypeError(samlObject.getClass().getName());
}
StatusResponseType statusResponseType = (StatusResponseType) samlObject;
String issuer = statusResponseType.getIssuer().getValue();
boolean isValid = samlResponseMessage != null;
if (!isValid)
throw logger.samlIDPValidationCheckFailed();
IssuerInfoHolder idpIssuer = new IssuerInfoHolder(getIdentityURL());
ProtocolContext protocolContext = new HTTPContext(request, response, getContext().getServletContext());
// Create the request/response
SAML2HandlerRequest saml2HandlerRequest = new DefaultSAML2HandlerRequest(protocolContext, idpIssuer.getIssuer(),
samlDocumentHolder, HANDLER_TYPE.IDP);
Map<String, Object> options = new HashMap<String, Object>();
if (this.idpConfiguration.isSupportsSignature() || this.idpConfiguration.isEncrypt()) {
PublicKey publicKey = getIssuerPublicKey(request, issuer);
options.put(GeneralConstants.SENDER_PUBLIC_KEY, publicKey);
}
options.put(GeneralConstants.SAML_IDP_STRICT_POST_BINDING, this.idpConfiguration.isStrictPostBinding());
options.put(GeneralConstants.SUPPORTS_SIGNATURES, this.idpConfiguration.isSupportsSignature());
if (auditHelper != null) {
options.put(GeneralConstants.AUDIT_HELPER, auditHelper);
options.put(GeneralConstants.CONTEXT_PATH, contextPath);
}
saml2HandlerRequest.setOptions(options);
saml2HandlerRequest.setRelayState(relayState);
SAML2HandlerResponse saml2HandlerResponse = new DefaultSAML2HandlerResponse();
Set<SAML2Handler> handlers = chain.handlers();
// the trusted domains is done by a handler
// webRequestUtil.isTrusted(issuer);
if (handlers != null) {
try {
chainLock.lock();
for (SAML2Handler handler : handlers) {
handler.reset();
handler.handleStatusResponseType(saml2HandlerRequest, saml2HandlerResponse);
willSendRequest = saml2HandlerResponse.getSendRequest();
}
} finally {
chainLock.unlock();
}
}
samlResponse = saml2HandlerResponse.getResultingDocument();
relayState = saml2HandlerResponse.getRelayState();
destination = saml2HandlerResponse.getDestination();
requestedPostProfile = saml2HandlerResponse.isPostBindingForResponse();
destinationQueryStringWithSignature = saml2HandlerResponse.getDestinationQueryStringWithSignature();
} catch (Exception e) {
String status = JBossSAMLURIConstants.STATUS_AUTHNFAILED.get();
if (e instanceof IssuerNotTrustedException) {
status = JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get();
}
logger.samlIDPRequestProcessingError(e);
samlResponse = webRequestUtil.getErrorResponse(referer, status, getIdentityURL(),
this.idpConfiguration.isSupportsSignature());
isErrorResponse = true;
} finally {
try {
WebRequestUtilHolder holder = webRequestUtil.getHolder();
if (destination == null)
throw new ServletException(logger.nullValueError("Destination"));
holder.setResponseDoc(samlResponse).setDestination(destination).setRelayState(relayState)
.setAreWeSendingRequest(willSendRequest).setPrivateKey(null).setSupportSignature(false)
.setErrorResponse(isErrorResponse).setServletResponse(response)
.setPostBindingRequested(requestedPostProfile)
.setDestinationQueryStringWithSignature(destinationQueryStringWithSignature);
/*
* if (requestedPostProfile) holder.setPostBindingRequested(requestedPostProfile); else
* holder.setPostBindingRequested(postProfile);
*/
if (this.idpConfiguration.isSupportsSignature()) {
holder.setPrivateKey(keyManager.getSigningKey()).setSupportSignature(true);
}
holder.setStrictPostBinding(this.idpConfiguration.isStrictPostBinding());
if (holder.isPostBinding())
recycle(response);
if (enableAudit) {
PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
auditEvent.setType(PicketLinkAuditEventType.RESPONSE_TO_SP);
auditEvent.setWhoIsAuditing(contextPath);
auditEvent.setDestination(destination);
auditHelper.audit(auditEvent);
}
webRequestUtil.send(holder);
} catch (ParsingException e) {
logger.samlAssertionPasingFailed(e);
} catch (GeneralSecurityException e) {
logger.trace("Security Exception:", e);
}
}
return;
}
protected void cleanUpSessionNote(Request request) {
Session session = request.getSessionInternal();
/**
* Since the container has finished the authentication, we can retrieve the original saml message as well as any relay
* state from the SP
*/
String samlRequestMessage = (String) session.getNote(GeneralConstants.SAML_REQUEST_KEY);
String samlResponseMessage = (String) session.getNote(GeneralConstants.SAML_RESPONSE_KEY);
String relayState = (String) session.getNote(GeneralConstants.RELAY_STATE);
String signature = (String) session.getNote(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
String sigAlg = (String) session.getNote(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
if (logger.isTraceEnabled()) {
StringBuilder builder = new StringBuilder();
builder.append("Retrieved saml messages and relay state from session");
builder.append("saml Request message=").append(samlRequestMessage);
builder.append("::").append("SAMLResponseMessage=");
builder.append(samlResponseMessage).append(":").append("relay state=").append(relayState);
builder.append("Signature=").append(signature).append("::sigAlg=").append(sigAlg);
logger.trace(builder.toString());
}
if (isNotNull(samlRequestMessage))
session.removeNote(GeneralConstants.SAML_REQUEST_KEY);
if (isNotNull(samlResponseMessage))
session.removeNote(GeneralConstants.SAML_RESPONSE_KEY);
if (isNotNull(relayState))
session.removeNote(GeneralConstants.RELAY_STATE);
if (isNotNull(signature))
session.removeNote(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
if (isNotNull(sigAlg))
session.removeNote(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
}
protected void sendErrorResponseToSP(String referrer, Response response, String relayState, IDPWebRequestUtil webRequestUtil)
throws ServletException, IOException, ConfigurationException {
logger.trace("About to send error response to SP:" + referrer);
String contextPath = getContextPath();
Document samlResponse = webRequestUtil.getErrorResponse(referrer, JBossSAMLURIConstants.STATUS_RESPONDER.get(),
getIdentityURL(), this.idpConfiguration.isSupportsSignature());
try {
WebRequestUtilHolder holder = webRequestUtil.getHolder();
holder.setResponseDoc(samlResponse).setDestination(referrer).setRelayState(relayState)
.setAreWeSendingRequest(false).setPrivateKey(null).setSupportSignature(false).setServletResponse(response);
holder.setPostBindingRequested(webRequestUtil.hasSAMLRequestInPostProfile());
if (this.idpConfiguration.isSupportsSignature()) {
holder.setPrivateKey(keyManager.getSigningKey()).setSupportSignature(true);
}
holder.setStrictPostBinding(this.idpConfiguration.isStrictPostBinding());
if (holder.isPostBinding())
recycle(response);
if (enableAudit) {
PicketLinkAuditEvent auditEvent = new PicketLinkAuditEvent(AuditLevel.INFO);
auditEvent.setType(PicketLinkAuditEventType.ERROR_RESPONSE_TO_SP);
auditEvent.setWhoIsAuditing(contextPath);
auditEvent.setDestination(referrer);
auditHelper.audit(auditEvent);
}
webRequestUtil.send(holder);
} catch (ParsingException e1) {
throw new ServletException(e1);
} catch (GeneralSecurityException e) {
throw new ServletException(e);
}
}
/**
* <p>
* Initializes the {@link IdentityServer}.
* </p>
*/
protected void initIdentityServer() {
// The Identity Server on the servlet context gets set
// in the implementation of IdentityServer
// Create an Identity Server and set it on the context
IdentityServer identityServer = (IdentityServer) getContext().getServletContext().getAttribute(
GeneralConstants.IDENTITY_SERVER);
if (identityServer == null) {
identityServer = new IdentityServer();
getContext().getServletContext().setAttribute(GeneralConstants.IDENTITY_SERVER, identityServer);
if (StringUtil.isNotNull(this.idpConfiguration.getIdentityParticipantStack())) {
try {
Class<?> clazz = SecurityActions.loadClass(getClass(), this.idpConfiguration.getIdentityParticipantStack());
if (clazz == null)
throw logger.classNotLoadedError(this.idpConfiguration.getIdentityParticipantStack());
identityServer.setStack((IdentityParticipantStack) clazz.newInstance());
} catch (Exception e) {
logger.samlIDPUnableToSetParticipantStackUsingDefault(e);
}
}
}
}
/**
* <p>
* Initialize the Handlers chain.
* </p>
*
* @throws LifecycleException
*/
protected void initHandlersChain() throws LifecycleException {
Handlers handlers = null;
try {
if (picketLinkConfiguration != null) {
handlers = picketLinkConfiguration.getHandlers();
} else {
// Get the handlers
String handlerConfigFileName = GeneralConstants.HANDLER_CONFIG_FILE_LOCATION;
handlers = ConfigurationUtil.getHandlers(getContext().getServletContext().getResourceAsStream(
handlerConfigFileName));
}
// Get the chain from config
String handlerChainClass = handlers.getHandlerChainClass();
if (StringUtil.isNullOrEmpty(handlerChainClass))
chain = SAML2HandlerChainFactory.createChain();
else {
try {
chain = SAML2HandlerChainFactory.createChain(handlerChainClass);
} catch (ProcessingException e1) {
throw new LifecycleException(e1);
}
}
chain.addAll(HandlerUtil.getHandlers(handlers));
Map<String, Object> chainConfigOptions = new HashMap<String, Object>();
chainConfigOptions.put(GeneralConstants.ROLE_GENERATOR, roleGenerator);
chainConfigOptions.put(GeneralConstants.CONFIGURATION, idpConfiguration);
if (this.keyManager != null){
chainConfigOptions.put(GeneralConstants.KEYPAIR, keyManager.getSigningKeyPair());
String certAlias = (String) keyManager.getAdditionalOption(GeneralConstants.X509CERTIFICATE);
if( certAlias != null){
chainConfigOptions.put(GeneralConstants.X509CERTIFICATE, keyManager.getCertificate(certAlias));
}
}
SAML2HandlerChainConfig handlerChainConfig = new DefaultSAML2HandlerChainConfig(chainConfigOptions);
Set<SAML2Handler> samlHandlers = chain.handlers();
for (SAML2Handler handler : samlHandlers) {
handler.initChainConfig(handlerChainConfig);
}
} catch (Exception e) {
logger.samlHandlerConfigurationError(e);
throw new LifecycleException(e.getLocalizedMessage());
}
}
protected void initKeyManager() throws LifecycleException {
if (this.idpConfiguration.isSupportsSignature() || this.idpConfiguration.isEncrypt()) {
KeyProviderType keyProvider = this.idpConfiguration.getKeyProvider();
if (keyProvider == null)
throw new LifecycleException(
logger.nullValueError("Key Provider is null for context=" + getContext().getName()));
try {
this.keyManager = CoreConfigUtil.getTrustKeyManager(keyProvider);
List<AuthPropertyType> authProperties = CoreConfigUtil.getKeyProviderProperties(keyProvider);
keyManager.setAuthProperties(authProperties);
keyManager.setValidatingAlias(keyProvider.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
keyManager.addAdditionalOption(GeneralConstants.X509CERTIFICATE, authPropertyType.getValue());
break;
}
}
}
} catch (Exception e) {
logger.trustKeyManagerCreationError(e);
throw new LifecycleException(e.getLocalizedMessage());
}
logger.samlIDPSettingCanonicalizationMethod(idpConfiguration.getCanonicalizationMethod());
XMLSignatureUtil.setCanonicalizationMethodType(idpConfiguration.getCanonicalizationMethod());
logger.trace("Key Provider=" + keyProvider.getClassName());
}
}
/**
* <p>
* Initializes the IDP configuration.
* </p>
*/
@SuppressWarnings("deprecation")
protected void initIDPConfiguration() {
String configFile = GeneralConstants.CONFIG_FILE_LOCATION;
InputStream is = getContext().getServletContext().getResourceAsStream(configFile);
// Work on the IDP Configuration
if (configProvider != null) {
try {
if (is == null) {
// Try the older version
is = getContext().getServletContext().getResourceAsStream(GeneralConstants.DEPRECATED_CONFIG_FILE_LOCATION);
// Additionally parse the deprecated config file
if (is != null && configProvider instanceof AbstractSAMLConfigurationProvider) {
((AbstractSAMLConfigurationProvider) configProvider).setConfigFile(is);
}
} else {
// Additionally parse the consolidated config file
if (is != null && configProvider instanceof AbstractSAMLConfigurationProvider) {
((AbstractSAMLConfigurationProvider) configProvider).setConsolidatedConfigFile(is);
}
}
picketLinkConfiguration = configProvider.getPicketLinkConfiguration();
idpConfiguration = configProvider.getIDPConfiguration();
} catch (ProcessingException e) {
throw logger.samlIDPConfigurationError(e);
} catch (ParsingException e) {
throw logger.samlIDPConfigurationError(e);
}
}
if (idpConfiguration == null) {
if (is != null) {
try {
picketLinkConfiguration = ConfigurationUtil.getConfiguration(is);
idpConfiguration = (IDPType) picketLinkConfiguration.getIdpOrSP();
} catch (ParsingException e) {
logger.trace(e);
logger.samlIDPConfigurationError(e);
}
}
if (is == null) {
// Try the older version
is = getContext().getServletContext().getResourceAsStream(GeneralConstants.DEPRECATED_CONFIG_FILE_LOCATION);
if (is == null)
throw logger.configurationFileMissing(configFile);
try {
idpConfiguration = ConfigurationUtil.getIDPConfiguration(is);
} catch (ParsingException e) {
logger.samlIDPConfigurationError(e);
}
}
}
try {
if (this.picketLinkConfiguration != null) {
enableAudit = picketLinkConfiguration.isEnableAudit();
// See if we have the system property enabled
if (!enableAudit) {
String sysProp = SecurityActions.getSystemProperty(GeneralConstants.AUDIT_ENABLE, "NULL");
if (!"NULL".equals(sysProp)) {
enableAudit = Boolean.parseBoolean(sysProp);
}
}
if (enableAudit) {
if (auditHelper == null) {
String securityDomainName = PicketLinkAuditHelper.getSecurityDomainName(getContext()
.getServletContext());
auditHelper = new PicketLinkAuditHelper(securityDomainName);
}
}
}
logger.trace("Identity Provider URL=" + getIdentityURL());
// Get the attribute manager
String attributeManager = idpConfiguration.getAttributeManager();
if (attributeManager != null && !"".equals(attributeManager)) {
Class<?> clazz = SecurityActions.loadClass(getClass(), attributeManager);
if (clazz == null)
throw new RuntimeException(logger.classNotLoadedError(attributeManager));
AttributeManager delegate = (AttributeManager) clazz.newInstance();
this.attribManager.setDelegate(delegate);
}
// Get the role generator
String roleGeneratorAttribute = idpConfiguration.getRoleGenerator();
if (roleGeneratorAttribute != null && !"".equals(roleGeneratorAttribute)) {
Class<?> clazz = SecurityActions.loadClass(getClass(), roleGeneratorAttribute);
if (clazz == null)
throw new RuntimeException(logger.classNotLoadedError(roleGeneratorAttribute));
roleGenerator = (RoleGenerator) clazz.newInstance();
}
// Read SP Metadata if provided
List<EntityDescriptorType> entityDescriptors = CoreConfigUtil.getMetadataConfiguration(idpConfiguration,
getContext().getServletContext());
if (entityDescriptors != null) {
for (EntityDescriptorType entityDescriptorType : entityDescriptors) {
SPSSODescriptorType spSSODescriptor = CoreConfigUtil.getSPDescriptor(entityDescriptorType);
if (spSSODescriptor != null) {
spSSOMetadataMap.put(entityDescriptorType.getEntityID(), spSSODescriptor);
}
}
}
} catch (Exception e) {
throw logger.samlIDPConfigurationError(e);
}
initHostedURI();
}
/**
* Initializes the STS configuration.
*/
protected void initSTSConfiguration() {
// if the sts configuration is present in the picketlink.xml then load it.
if (this.picketLinkConfiguration != null && this.picketLinkConfiguration.getStsType() != null) {
PicketLinkCoreSTS sts = PicketLinkCoreSTS.instance();
sts.initialize(new PicketLinkSTSConfiguration(this.picketLinkConfiguration.getStsType()));
} else {
// Try to load from /WEB-INF/picketlink-sts.xml.
// Ensure that the Core STS has the SAML20 Token Provider
PicketLinkCoreSTS sts = PicketLinkCoreSTS.instance();
// Let us look for a file
String configPath = getContext().getServletContext().getRealPath("/WEB-INF/picketlink-sts.xml");
File stsTokenConfigFile = configPath != null ? new File(configPath) : null;
if (stsTokenConfigFile == null || stsTokenConfigFile.exists() == false) {
logger.samlIDPInstallingDefaultSTSConfig();
sts.installDefaultConfiguration();
} else
sts.installDefaultConfiguration(stsTokenConfigFile.toURI().toString());
}
}
protected String getIdentityURL() {
return this.idpConfiguration.getIdentityURL();
}
protected Context getContext() {
return (Context) getContainer();
}
protected abstract String getContextPath();
protected void recycle(Response response) {
/**
* Since the container finished authentication, it will try to locate index.jsp or index.html. We need to recycle
* whatever is in the response object such that we direct it to the html that is being created as part of the HTTP/POST
* binding
*/
response.recycle();
}
protected String determineLoginType(boolean isSecure) {
String result = JBossSAMLURIConstants.AC_PASSWORD.get();
LoginConfig loginConfig = getContext().getLoginConfig();
if (loginConfig != null) {
String auth = loginConfig.getAuthMethod();
if (StringUtil.isNotNull(auth)) {
if ("CLIENT-CERT".equals(auth))
result = JBossSAMLURIConstants.AC_TLS_CLIENT.get();
else if (isSecure)
result = JBossSAMLURIConstants.AC_PASSWORD_PROTECTED_TRANSPORT.get();
}
}
return result;
}
protected void startPicketLink() throws LifecycleException {
SystemPropertiesUtil.ensure();
initIDPConfiguration();
initSTSConfiguration();
initKeyManager();
initHandlersChain();
initIdentityServer();
// Add some keys to the attibutes
String[] ak = new String[] { "mail", "cn", "commonname", "givenname", "surname", "employeeType", "employeeNumber",
"facsimileTelephoneNumber" };
this.attributeKeys.addAll(Arrays.asList(ak));
}
/**
* Given a set of roles, create an attribute statement
*
* @param roles
* @return
*/
private SAML11AttributeStatementType createAttributeStatement(List<String> roles) {
SAML11AttributeStatementType attrStatement = null;
for (String role : roles) {
if (attrStatement == null) {
attrStatement = new SAML11AttributeStatementType();
}
SAML11AttributeType attr = new SAML11AttributeType("Role", URI.create("urn:picketlink:role"));
attr.add(role);
attrStatement.add(attr);
}
return attrStatement;
}
public void setAuditHelper(PicketLinkAuditHelper auditHelper) {
this.auditHelper = auditHelper;
}
/**
* We will ignore signatures of current SAMLRequest if SP Metadata are provided for current SP and if metadata specifies
* that SAMLRequest is not signed for this SP.
*
* @param spIssuer
* @return true if signature is not expected in SAMLRequest and so signature validation should be ignored
*/
private Boolean willIgnoreSignatureOfCurrentRequest(String spIssuer) {
SPSSODescriptorType currentSPMetadata = spSSOMetadataMap.get(spIssuer);
if (currentSPMetadata == null) {
return false;
}
Boolean isRequestSigned = currentSPMetadata.isAuthnRequestsSigned();
logger.trace("Issuer: " + spIssuer + ", isRequestSigned: " + isRequestSigned);
return !isRequestSigned;
}
private void initHostedURI() {
String hostedURI = this.idpConfiguration.getHostedURI();
if (isNullOrEmpty(hostedURI)) {
hostedURI = "/hosted/";
} else if (!hostedURI.contains(".") && !hostedURI.endsWith("/")) {
// make sure the hosted uri have a slash at the end if it points to a directory
hostedURI = hostedURI + "/";
}
this.idpConfiguration.setHostedURI(hostedURI);
}
private SSLAuthenticator getSSLAuthenticator() {
if (this.sslAuthenticator == null) {
this.sslAuthenticator = new SSLAuthenticator() {
@Override
public Valve getNext() {
return new ValveBase() {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
// no-op
}
};
}
};
this.sslAuthenticator.setContainer(getContainer());
try {
this.sslAuthenticator.start();
} catch (LifecycleException e) {
throw new RuntimeException("Error starting SSL authenticator.", e);
}
}
return this.sslAuthenticator;
}
}