/* * 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.web.process; import org.picketlink.common.PicketLinkLogger; import org.picketlink.common.PicketLinkLoggerFactory; import org.picketlink.common.constants.GeneralConstants; import org.picketlink.common.exceptions.ConfigurationException; import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.common.exceptions.TrustKeyConfigurationException; import org.picketlink.common.exceptions.TrustKeyProcessingException; import org.picketlink.config.federation.PicketLinkType; import org.picketlink.config.federation.ProviderType; import org.picketlink.config.federation.SPType; import org.picketlink.identity.federation.core.audit.PicketLinkAuditHelper; import org.picketlink.identity.federation.core.interfaces.TrustKeyManager; import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder; import org.picketlink.identity.federation.core.saml.v2.holders.IssuerInfoHolder; 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.SAML2Handler; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler.HANDLER_TYPE; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerRequest; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerRequest.GENERATE_REQUEST_TYPE; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerResponse; import org.picketlink.identity.federation.core.saml.v2.util.SAMLMetadataUtil; import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType; import org.picketlink.identity.federation.web.core.HTTPContext; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URL; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import static org.picketlink.common.util.StringUtil.isNotNull; import static org.picketlink.common.util.StringUtil.isNullOrEmpty; /** * A processor util at the SP * * @author Anil.Saldhana@redhat.com * @since Oct 27, 2009 */ public class ServiceProviderBaseProcessor { protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); protected final PicketLinkType configuration; private final IDPSSODescriptorType idpMetadata; protected boolean postBinding; protected String serviceURL; protected String identityURL; protected TrustKeyManager keyManager; protected String issuer = null; protected PicketLinkAuditHelper auditHelper = null; public static final String IDP_KEY = "idp.key"; /** * Construct * * @param postBinding Whether it is the Post Binding * @param serviceURL Service URL of the SP */ public ServiceProviderBaseProcessor(boolean postBinding, String serviceURL, PicketLinkType configuration) { this(postBinding, serviceURL, configuration, null); } public ServiceProviderBaseProcessor(boolean postBinding, String serviceURL, PicketLinkType configuration, IDPSSODescriptorType idpMetadata) { this.postBinding = postBinding; this.serviceURL = serviceURL; this.configuration = configuration; this.idpMetadata = idpMetadata; } /** * Set the {@code TrustKeyManager} * * @param tkm */ public void setTrustKeyManager(TrustKeyManager tkm) { this.keyManager = tkm; } /** * Set the Identity URL * * @param identityURL */ public void setIdentityURL(String identityURL) { this.identityURL = identityURL; } /** * Set a separate issuer that is different from the service url * * @param issuer */ public void setIssuer(String issuer) { this.issuer = issuer; } /** * Set the {@link PicketLinkAuditHelper} * * @param helper */ public void setAuditHelper(PicketLinkAuditHelper helper) { this.auditHelper = helper; } public SAML2HandlerResponse process(HTTPContext httpContext, Set<SAML2Handler> handlers, Lock chainLock) throws ProcessingException, IOException, ParsingException, ConfigurationException { logger.trace("SAML Handlers are: " + handlers); // Neither saml request nor response from IDP // So this is a user request // Ask the handler chain to generate the saml request // Create the request/response SAML2HandlerRequest saml2HandlerRequest = getSAML2HandlerRequest(null, httpContext); saml2HandlerRequest.addOption(GeneralConstants.CONTEXT_PATH, httpContext.getServletContext().getContextPath()); saml2HandlerRequest.addOption(GeneralConstants.SUPPORTS_SIGNATURES, getSpConfiguration().isSupportsSignature()); SAML2HandlerResponse saml2HandlerResponse = new DefaultSAML2HandlerResponse(); saml2HandlerResponse.setPostBindingForResponse(postBinding); saml2HandlerResponse.setDestination(identityURL); // if the request is a GLO. Check if there is a specific URL for logout. if (isLogOutRequest(httpContext)) { String logoutUrl = ((SPType) getSpConfiguration()).getLogoutUrl(); if (logoutUrl != null) { saml2HandlerResponse.setDestination(logoutUrl); } } // Reset the state try { if (this.configuration.getHandlers().isLocking()) { chainLock.lock(); } for (SAML2Handler handler : handlers) { handler.reset(); if (saml2HandlerResponse.isInError()) { httpContext.getResponse().sendError(saml2HandlerResponse.getErrorCode()); break; } if (isLogOutRequest(httpContext)) saml2HandlerRequest.setTypeOfRequestToBeGenerated(GENERATE_REQUEST_TYPE.LOGOUT); else saml2HandlerRequest.setTypeOfRequestToBeGenerated(GENERATE_REQUEST_TYPE.AUTH); handler.generateSAMLRequest(saml2HandlerRequest, saml2HandlerResponse); logger.trace("Finished Processing handler: " + handler.getClass().getCanonicalName()); } } catch (ProcessingException pe) { logger.error(pe); throw logger.samlHandlerChainProcessingError(pe); } finally { if (this.configuration.getHandlers().isLocking()) { chainLock.unlock(); } } return saml2HandlerResponse; } protected ProviderType getSpConfiguration() { return this.configuration.getIdpOrSP(); } protected SAML2HandlerRequest getSAML2HandlerRequest(SAMLDocumentHolder documentHolder, HTTPContext httpContext) { IssuerInfoHolder holder = null; if (issuer == null) { holder = new IssuerInfoHolder(this.serviceURL); } else { holder = new IssuerInfoHolder(issuer); } return new DefaultSAML2HandlerRequest(httpContext, holder.getIssuer(), documentHolder, HANDLER_TYPE.SP); } protected boolean isLogOutRequest(HTTPContext httpContext) { HttpServletRequest request = httpContext.getRequest(); String gloStr = request.getParameter(GeneralConstants.GLOBAL_LOGOUT); return isNotNull(gloStr) && "true".equalsIgnoreCase(gloStr); } protected URL safeURL(String urlString) { try { return new URL(urlString); } catch (Exception e) { } return null; } /** * <p> Returns the PublicKey to be used to verify signatures for SAML tokens issued by the IDP. </p> * * @return * * @throws org.picketlink.common.exceptions.TrustKeyConfigurationException * @throws org.picketlink.common.exceptions.TrustKeyProcessingException */ protected PublicKey getIDPPublicKey() throws TrustKeyConfigurationException, TrustKeyProcessingException { if (this.idpMetadata != null) { X509Certificate certificate = SAMLMetadataUtil.getCertificate(null, this.idpMetadata); if (certificate != null) { return certificate.getPublicKey(); } } if (this.keyManager == null) { throw logger.trustKeyManagerMissing(); } String idpValidatingAlias = (String) this.keyManager.getAdditionalOption(ServiceProviderBaseProcessor.IDP_KEY); if (isNullOrEmpty(idpValidatingAlias)) { idpValidatingAlias = safeURL(getSpConfiguration().getIdentityURL()).getHost(); } return keyManager.getValidatingKey(idpValidatingAlias); } protected void setRequestOptions(SAML2HandlerRequest saml2HandlerRequest) throws TrustKeyConfigurationException, TrustKeyProcessingException { Map<String, Object> requestOptions = new HashMap<String, Object>(); requestOptions.put(GeneralConstants.CONFIGURATION, getSpConfiguration()); requestOptions.put(GeneralConstants.IDP_SSO_METADATA_DESCRIPTOR, this.idpMetadata); requestOptions.put(GeneralConstants.SSO_METADATA_DESCRIPTOR, this.idpMetadata); if (auditHelper != null) { requestOptions.put(GeneralConstants.AUDIT_HELPER, auditHelper); } if (keyManager != null) { PublicKey validatingKey = getIDPPublicKey(); requestOptions.put(GeneralConstants.SENDER_PUBLIC_KEY, validatingKey); requestOptions.put(GeneralConstants.DECRYPTING_KEY, keyManager.getEncryptionKey()); } requestOptions.put(GeneralConstants.SUPPORTS_SIGNATURES, getSpConfiguration().isSupportsSignature()); saml2HandlerRequest.setOptions(requestOptions); } }