/*
* 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.saml.workflow;
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.ProcessingException;
import org.picketlink.common.util.DocumentUtil;
import org.picketlink.identity.federation.core.saml.v2.holders.DestinationInfoHolder;
import org.picketlink.identity.federation.web.util.HTTPRedirectUtil;
import org.picketlink.identity.federation.web.util.PostBindingUtil;
import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
import org.w3c.dom.Document;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import static org.picketlink.common.util.StringUtil.isNotNull;
/**
* Common methods used by the SAML Service Provider
* @author Anil Saldhana
* @since December 03, 2013
*/
public class ServiceProviderSAMLWorkflow {
private RedirectionHandler redirectionHandler = new RedirectionHandler();
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
/**
* Set a web container specific {@link org.picketlink.identity.federation.core.saml.workflow.ServiceProviderSAMLWorkflow.RedirectionHandler}
* @param theHandler
* @return {@link org.picketlink.identity.federation.core.saml.workflow.ServiceProviderSAMLWorkflow} for chaining methods
*/
public ServiceProviderSAMLWorkflow setRedirectionHandler(RedirectionHandler theHandler){
this.redirectionHandler = theHandler;
return this;
}
/**
* Perform validation of the request object
*
* @param request
* @return
*/
public boolean validate(HttpServletRequest request) {
return request.getParameter("SAMLResponse") != null;
}
/**
* <p>
* Indicates if the current request is a GlobalLogout request.
* </p>
*
* @param request
* @return
*/
public boolean isGlobalLogout(HttpServletRequest request) {
String gloStr = request.getParameter(GeneralConstants.GLOBAL_LOGOUT);
return isNotNull(gloStr) && "true".equalsIgnoreCase(gloStr);
}
/**
* Verify whether a {@link HttpServletRequest} is for Local Logout
* @param request
* @return
*/
public boolean isLocalLogoutRequest(HttpServletRequest request){
String lloStr = request.getParameter(GeneralConstants.LOCAL_LOGOUT);
return isNotNull(lloStr) && "true".equalsIgnoreCase(lloStr);
}
public void sendToLogoutPage(HttpServletRequest request, HttpServletResponse response, HttpSession session,
ServletContext servletContext,
String logOutPage) throws IOException, ServletException {
// we are invalidated.
RequestDispatcher dispatch = servletContext.getRequestDispatcher(logOutPage);
if (dispatch == null)
logger.samlSPCouldNotDispatchToLogoutPage(logOutPage);
else {
logger.trace("Forwarding request to logOutPage: " + logOutPage);
if (request.getSession(false) != null) {
session.invalidate();
}
try {
dispatch.forward(request, response);
} catch (Exception e) {
// JBAS5.1 and 6 quirkiness
//dispatch.forward(request.getRequest(), response);
}
}
}
/**
* <p>
* Send the request to the IDP. Subclasses should override this method to implement how requests must be sent to the IDP.
* </p>
*
* @param destination idp url
* @param samlDocument request or response document
* @param relayState
* @param response
* @param willSendRequest are we sending Request or Response to IDP
* @param destinationQueryStringWithSignature used only with Redirect binding and with signature enabled.
* @param httpPostBinding
* @throws org.picketlink.common.exceptions.ProcessingException
* @throws org.picketlink.common.exceptions.ConfigurationException
* @throws IOException
*/
public void sendRequestToIDP(String destination, Document samlDocument, String relayState, HttpServletResponse response,
boolean willSendRequest, String destinationQueryStringWithSignature,
boolean httpPostBinding) throws ProcessingException, ConfigurationException, IOException {
if (httpPostBinding) {
sendHttpPostBindingRequest(destination, samlDocument, relayState, response, willSendRequest);
} else {
sendHttpRedirectRequest(destination, samlDocument, relayState, response, willSendRequest, destinationQueryStringWithSignature);
}
}
/**
* <p>
* Sends a HTTP Redirect request to the IDP.
* </p>
*
* @param destination
* @param relayState
* @param response
* @param willSendRequest
* @param destinationQueryStringWithSignature
* @throws IOException
* @throws java.io.UnsupportedEncodingException
* @throws ConfigurationException
* @throws ProcessingException
*/
public void sendHttpRedirectRequest(String destination, Document samlDocument, String relayState, HttpServletResponse response,
boolean willSendRequest, String destinationQueryStringWithSignature) throws IOException,
ProcessingException, ConfigurationException {
String destinationQueryString = null;
// We already have queryString with signature from SAML2SignatureGenerationHandler
if (destinationQueryStringWithSignature != null) {
destinationQueryString = destinationQueryStringWithSignature;
}
else {
String samlMessage = DocumentUtil.getDocumentAsString(samlDocument);
String base64Request = RedirectBindingUtil.deflateBase64URLEncode(samlMessage.getBytes("UTF-8"));
destinationQueryString = RedirectBindingUtil.getDestinationQueryString(base64Request, relayState, willSendRequest);
}
RedirectBindingUtil.RedirectBindingUtilDestHolder holder = new RedirectBindingUtil.RedirectBindingUtilDestHolder();
holder.setDestination(destination).setDestinationQueryString(destinationQueryString);
//HTTPRedirectUtil.sendRedirectForRequestor(RedirectBindingUtil.getDestinationURL(holder), response);
redirectionHandler.sendRedirectForRequestor(RedirectBindingUtil.getDestinationURL(holder), response);
}
/**
* <p>
* Sends a HTTP POST request to the IDP.
* </p>
*
* @param destination
* @param samlDocument
* @param relayState
* @param response
* @param willSendRequest
* @throws org.picketlink.common.exceptions.TrustKeyProcessingException
* @throws ProcessingException
* @throws IOException
* @throws ConfigurationException
*/
public void sendHttpPostBindingRequest(String destination, Document samlDocument, String relayState, HttpServletResponse response,
boolean willSendRequest) throws ProcessingException, IOException,
ConfigurationException {
String samlMessage = PostBindingUtil.base64Encode(DocumentUtil.getDocumentAsString(samlDocument));
DestinationInfoHolder destinationHolder = new DestinationInfoHolder(destination, samlMessage, relayState);
//PostBindingUtil.sendPost(destinationHolder, response, willSendRequest);
redirectionHandler.sendPost(destinationHolder, response, willSendRequest);
}
/**
* Class that handles the web container specific behavior for POST
* and REDIRECT workflows
*/
public static class RedirectionHandler{
/**
* Send the payload via HTTP/POST
* @param destinationHolder {@link org.picketlink.identity.federation.core.saml.v2.holders.DestinationInfoHolder} holds info on the destination
* @param response {@link javax.servlet.http.HttpServletResponse}
* @param willSendRequest whether it is a SAML request or response so that the page title can be set
* @throws IOException
*/
public void sendPost(DestinationInfoHolder destinationHolder,HttpServletResponse response, boolean willSendRequest) throws IOException{
PostBindingUtil.sendPost(destinationHolder, response, willSendRequest);
}
/**
* Send the payload via HTTP/REDIRECT
* @param url the redirect url
* @param response {@link javax.servlet.http.HttpServletResponse}
* @throws IOException
*/
public void sendRedirectForRequestor(String url, HttpServletResponse response) throws IOException {
HTTPRedirectUtil.sendRedirectForRequestor(url, response);
}
/**
* Send the payload via HTTP/REDIRECT
* @param destination the destination url
* @param response {@link javax.servlet.http.HttpServletResponse}
* @throws IOException
*/
public void sendRedirectForResponder(String destination, HttpServletResponse response) throws IOException {
HTTPRedirectUtil.sendRedirectForResponder(destination,response);
}
}
}