Author: anil.saldhana(a)jboss.com
Date: 2009-05-22 18:38:21 -0400 (Fri, 22 May 2009)
New Revision: 512
Added:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPSAMLDebugValve.java
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebBrowserSSOValve.java
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebRequestUtil.java
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPPostFormAuthenticator.java
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/util/PostBindingUtil.java
Log:
JBID-41: http post web browser profile
Added:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPSAMLDebugValve.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPSAMLDebugValve.java
(rev 0)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPSAMLDebugValve.java 2009-05-22
22:38:21 UTC (rev 512)
@@ -0,0 +1,59 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.jboss.identity.federation.bindings.tomcat.idp;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.log4j.Logger;
+
+/**
+ * Debug Valve on the IDP end that will
+ * inform whether the SP is sending the SAMLRequest or not
+ * properly
+ * @author Anil.Saldhana(a)redhat.com
+ * @since May 22, 2009
+ */
+public class IDPSAMLDebugValve extends ValveBase
+{
+ private static Logger log = Logger.getLogger(IDPSAMLDebugValve.class);
+
+ @Override
+ public void invoke(Request request, Response response)
+ throws IOException, ServletException
+ {
+ StringBuilder builder = new StringBuilder();
+ String param = request.getParameter("SAMLRequest");
+ builder.append("Method = " +
request.getMethod()).append("\n");
+ builder.append("SAMLRequest=" + param).append("\n");
+ builder.append("Parameter exists?="+ param !=
null).append("\n");
+ String debugInfo = builder.toString();
+ log.debug("SP Sent POST::"+ debugInfo);
+
+ getNext().invoke(request, response);
+ }
+
+}
Added:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebBrowserSSOValve.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebBrowserSSOValve.java
(rev 0)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebBrowserSSOValve.java 2009-05-22
22:38:21 UTC (rev 512)
@@ -0,0 +1,296 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.jboss.identity.federation.bindings.tomcat.idp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.valves.ValveBase;
+import org.apache.log4j.Logger;
+import org.jboss.identity.federation.bindings.config.IDPType;
+import org.jboss.identity.federation.bindings.interfaces.RoleGenerator;
+import org.jboss.identity.federation.bindings.tomcat.TomcatRoleGenerator;
+import org.jboss.identity.federation.bindings.util.ValveUtil;
+import org.jboss.identity.federation.core.exceptions.ParsingException;
+import org.jboss.identity.federation.core.saml.v2.constants.JBossSAMLURIConstants;
+import org.jboss.identity.federation.saml.v2.protocol.AuthnRequestType;
+import org.jboss.identity.federation.saml.v2.protocol.RequestAbstractType;
+import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
+
+/**
+ * Generic Web Browser SSO valve for the IDP
+ * @author Anil.Saldhana(a)redhat.com
+ * @since May 18, 2009
+ */
+public class IDPWebBrowserSSOValve extends ValveBase implements Lifecycle
+{
+ private static Logger log = Logger.getLogger(IDPWebBrowserSSOValve.class);
+
+ protected IDPType idpConfiguration = null;
+
+ private RoleGenerator rg = new TomcatRoleGenerator();
+
+ private long assertionValidity = 5000; // 5 seconds in miliseconds
+
+ private String identityURL = null;
+
+ @Override
+ public void invoke(Request request, Response response) throws IOException,
ServletException
+ {
+ String referer = request.getHeader("Referer");
+ String relayState = request.getParameter("RelayState");
+ String samlMessage = request.getParameter("SAMLRequest");
+ boolean containsSAMLRequestMessage = samlMessage != null;
+
+ Session session = request.getSessionInternal();
+
+ if(containsSAMLRequestMessage)
+ {
+ session.setNote("SAMLRequest", samlMessage);
+ if(relayState != null)
+ session.setNote("RelayState", relayState);
+ }
+
+ //Lets check if the user has been authenticated
+ Principal userPrincipal = request.getPrincipal();
+ if(userPrincipal == null)
+ {
+ try
+ {
+ //Next in the invocation chain
+ getNext().invoke(request, response);
+ }
+ finally
+ {
+ userPrincipal = request.getPrincipal();
+ referer = request.getHeader("Referer");
+ log.debug("Referer in finally block="+ referer);
+ }
+ }
+
+
+ IDPWebRequestUtil webRequestUtil = new IDPWebRequestUtil(request,
idpConfiguration);
+
+ //Look for unauthorized status
+ if(response.getStatus() == HttpServletResponse.SC_FORBIDDEN)
+ {
+ ResponseType errorResponseType =
+ webRequestUtil.getErrorResponse(referer,
+ JBossSAMLURIConstants.STATUS_AUTHNFAILED.get(),
+ this.identityURL);
+ try
+ {
+ webRequestUtil.send(errorResponseType, relayState, response);
+ }
+ catch (ParsingException e)
+ {
+ throw new ServletException(e);
+ }
+ return;
+ }
+
+ if(userPrincipal != null)
+ {
+ /**
+ * Since the container has finished the authentication,
+ * we can retrieve the original saml message as well as
+ * any relay state from the SP
+ */
+ samlMessage = (String) session.getNote("SAMLRequest");
+ relayState = (String) session.getNote("RelayState");
+ session.removeNote("SAMLRequest");
+ session.removeNote("RelayState");
+
+ //Send valid saml response after processing the request
+ if(samlMessage != null)
+ {
+ //Get the SAML Request Message
+ RequestAbstractType requestAbstractType = null;
+ try
+ {
+ requestAbstractType = webRequestUtil.getSAMLRequest(samlMessage);
+ this.validate(request);
+ webRequestUtil.isTrusted(requestAbstractType.getIssuer().getValue());
+
+ List<String> roles = rg.generateRoles(userPrincipal);
+
+ AuthnRequestType art = (AuthnRequestType) requestAbstractType;
+ ResponseType responseType =
+ webRequestUtil.getResponse(art.getAssertionConsumerServiceURL(),
+ userPrincipal, roles,
+ this.identityURL, this.assertionValidity);
+ webRequestUtil.send(responseType, relayState, response);
+
+ }
+ catch (Exception e)
+ {
+ log.error("Exception:" ,e);
+ if(requestAbstractType != null)
+ referer = requestAbstractType.getIssuer().getValue();
+ sendErrorResponseToSP(referer, response, relayState, webRequestUtil);
+ }
+ return;
+ }
+ else
+ {
+ log.error("No SAML Request Message");
+ log.trace("Referer="+referer);
+
+ sendErrorResponseToSP(referer, response, relayState, webRequestUtil);
+ }
+ }
+ }
+
+ protected void sendErrorResponseToSP(String referrer, Response response, String
relayState,
+ IDPWebRequestUtil webRequestUtil) throws ServletException, IOException
+ {
+ ResponseType errorResponseType =
+ webRequestUtil.getErrorResponse(referrer,
JBossSAMLURIConstants.STATUS_RESPONDER.get(),
+ this.identityURL);
+ try
+ {
+ webRequestUtil.send(errorResponseType, relayState, response);
+ }
+ catch (ParsingException e1)
+ {
+ throw new ServletException(e1);
+ }
+ }
+
+
+ //***************Lifecycle
+ /**
+ * The lifecycle event support for this component.
+ */
+ protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /**
+ * Has this component been started yet?
+ */
+ private boolean started = false;
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle.findLifecycleListeners();
+ }
+
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener The listener to add
+ */
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after
<code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @exception LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (started)
+ throw new LifecycleException
+ ("IDPRedirectValve already Started");
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ started = true;
+
+ String configFile = "/WEB-INF/jboss-idfed.xml";
+ Context context = (Context) getContainer();
+ InputStream is = context.getServletContext().getResourceAsStream(configFile);
+ if(is == null)
+ throw new RuntimeException(configFile + " missing");
+ try
+ {
+ idpConfiguration = ValveUtil.getIDPConfiguration(is);
+ this.identityURL = idpConfiguration.getIdentityURL();
+ log.trace("Identity Provider URL=" + this.identityURL);
+ this.assertionValidity = idpConfiguration.getAssertionValidity();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.
+ *
+ * @exception LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+ // Validate and update our current component state
+ if (!started)
+ throw new LifecycleException
+ ("IDPRedirectValve NotStarted");
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ started = false;
+ }
+
+ protected boolean validate(Request request) throws Exception
+ {
+ return request.getParameter("SAMLRequest") != null;
+ }
+
+ //Private Methods
+}
\ No newline at end of file
Added:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebRequestUtil.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebRequestUtil.java
(rev 0)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/idp/IDPWebRequestUtil.java 2009-05-22
22:38:21 UTC (rev 512)
@@ -0,0 +1,306 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.jboss.identity.federation.bindings.tomcat.idp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.security.Principal;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.bind.JAXBException;
+
+import org.apache.catalina.connector.Response;
+import org.apache.log4j.Logger;
+import org.jboss.identity.federation.api.saml.v2.common.IDGenerator;
+import org.jboss.identity.federation.api.saml.v2.request.SAML2Request;
+import org.jboss.identity.federation.api.saml.v2.response.SAML2Response;
+import org.jboss.identity.federation.bindings.config.IDPType;
+import org.jboss.identity.federation.bindings.config.TrustType;
+import org.jboss.identity.federation.bindings.util.HTTPRedirectUtil;
+import org.jboss.identity.federation.bindings.util.PostBindingUtil;
+import org.jboss.identity.federation.bindings.util.RedirectBindingUtil;
+import org.jboss.identity.federation.bindings.util.ValveUtil;
+import org.jboss.identity.federation.core.exceptions.ParsingException;
+import org.jboss.identity.federation.core.saml.v2.constants.JBossSAMLURIConstants;
+import org.jboss.identity.federation.core.saml.v2.exceptions.IssuerNotTrustedException;
+import org.jboss.identity.federation.core.saml.v2.holders.IDPInfoHolder;
+import org.jboss.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
+import org.jboss.identity.federation.core.saml.v2.holders.SPInfoHolder;
+import org.jboss.identity.federation.saml.v2.assertion.AssertionType;
+import org.jboss.identity.federation.saml.v2.assertion.AttributeStatementType;
+import org.jboss.identity.federation.saml.v2.protocol.RequestAbstractType;
+import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
+import org.xml.sax.SAXException;
+
+/**
+ * Request Util
+ * <b> Not thread safe</b>
+ * @author Anil.Saldhana(a)redhat.com
+ * @since May 18, 2009
+ */
+public class IDPWebRequestUtil
+{
+ private static Logger log = Logger.getLogger(IDPWebRequestUtil.class);
+
+ private HttpServletRequest request;
+
+ private boolean redirectProfile = false;
+ private boolean postProfile = false;
+
+ private IDPType idpConfiguration;
+
+ public IDPWebRequestUtil(HttpServletRequest request, IDPType idp)
+ {
+ this.request = request;
+ this.idpConfiguration = idp;
+ hasSAMLRequestInRedirectProfile();
+ hasSAMLRequestInPostProfile();
+ }
+
+ public boolean hasSAMLRequestInRedirectProfile()
+ {
+ if("GET".equalsIgnoreCase(request.getMethod()))
+ {
+ redirectProfile = request.getParameter("SAMLRequest") != null;
+ }
+ return redirectProfile;
+ }
+
+ public boolean hasSAMLRequestInPostProfile()
+ {
+ if("POST".equalsIgnoreCase(request.getMethod()))
+ {
+ postProfile = request.getParameter("SAMLRequest") != null;
+ }
+ return postProfile;
+ }
+
+ public RequestAbstractType getSAMLRequest(String samlMessage) throws Exception
+ {
+ InputStream is = null;
+ SAML2Request saml2Request = new SAML2Request();
+ if(redirectProfile)
+ {
+ is = RedirectBindingUtil.base64DeflateDecode(samlMessage);
+ }
+ else
+ {
+ byte[] samlBytes = PostBindingUtil.base64Decode(samlMessage);
+ log.trace("SAMLRequest=" + new String(samlBytes));
+ is = new ByteArrayInputStream(samlBytes);
+ }
+
+ return saml2Request.getRequestType(is);
+ }
+
+
+ public ResponseType getResponse( String assertionConsumerURL,
+ Principal userPrincipal,
+ List<String> roles,
+ String identityURL,
+ long assertionValidity) throws Exception
+ {
+ ResponseType responseType = null;
+
+ SAML2Response saml2Response = new SAML2Response();
+
+ //Create a response type
+ String id = IDGenerator.create("ID_");
+
+ IssuerInfoHolder issuerHolder = new IssuerInfoHolder(identityURL);
+ issuerHolder.setStatusCode(JBossSAMLURIConstants.STATUS_SUCCESS.get());
+
+ IDPInfoHolder idp = new IDPInfoHolder();
+ idp.setNameIDFormatValue(userPrincipal.getName());
+ idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+
+ SPInfoHolder sp = new SPInfoHolder();
+ sp.setResponseDestinationURI(assertionConsumerURL);
+ responseType = saml2Response.createResponseType(id, sp, idp, issuerHolder);
+
+
+ //Add information on the roles
+ AssertionType assertion = (AssertionType)
responseType.getAssertionOrEncryptedAssertion().get(0);
+
+ AttributeStatementType attrStatement =
saml2Response.createAttributeStatement(roles);
+
assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement().add(attrStatement);
+
+ //Add timed conditions
+ saml2Response.createTimedConditions(assertion, assertionValidity);
+
+ //Lets see how the response looks like
+ if(log.isTraceEnabled())
+ {
+ StringWriter sw = new StringWriter();
+ saml2Response.marshall(responseType, sw);
+ log.trace("Response="+sw.toString());
+ }
+
+ return responseType;
+ }
+
+
+
+ /**
+ * Verify that the issuer is trusted
+ * @param issuer
+ * @throws IssuerNotTrustedException
+ */
+ public void isTrusted(String issuer) throws IssuerNotTrustedException
+ {
+ try
+ {
+ String issuerDomain = ValveUtil.getDomain(issuer);
+ TrustType idpTrust = idpConfiguration.getTrust();
+ if(idpTrust != null)
+ {
+ String domainsTrusted = idpTrust.getDomains();
+ if(domainsTrusted.indexOf(issuerDomain) < 0)
+ throw new IssuerNotTrustedException(issuer);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IssuerNotTrustedException(e.getLocalizedMessage(),e);
+ }
+ }
+
+
+ public void send(ResponseType responseType, String relayState,
+ Response response) throws IOException, ParsingException
+ {
+
+ SAML2Response saml2Response = new SAML2Response();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try
+ {
+ saml2Response.marshall(responseType, baos);
+ }
+ catch (SAXException e1)
+ {
+ throw new ParsingException(e1);
+ }
+ catch (JAXBException e1)
+ {
+ throw new ParsingException(e1);
+ }
+
+ if(redirectProfile)
+ {
+ String urlEncodedResponse =
RedirectBindingUtil.deflateBase64URLEncode(baos.toByteArray());
+
+ String destination = responseType.getDestination();
+ log.trace("IDP:Destination=" + destination);
+
+ if(relayState != null && relayState.length() > 0)
+ relayState = RedirectBindingUtil.urlEncode(relayState);
+
+ String finalDest = destination + getDestination(urlEncodedResponse,
relayState);
+ HTTPRedirectUtil.sendRedirectForResponder(finalDest, response);
+ }
+ else
+ {
+ /**
+ * 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();
+ String samlResponse = PostBindingUtil.base64Encode(baos.toString());
+ PostBindingUtil.sendPost(responseType.getDestination(),
+ samlResponse, relayState, response, false);
+ }
+ }
+
+ /**
+ * Generate a Destination URL for the HTTPRedirect binding
+ * with the saml response and relay state
+ * @param urlEncodedResponse
+ * @param urlEncodedRelayState
+ * @return
+ */
+ public String getDestination(String urlEncodedResponse, String urlEncodedRelayState)
+ {
+ if(redirectProfile)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("?SAMLResponse=").append(urlEncodedResponse);
+ if(urlEncodedRelayState != null && urlEncodedRelayState.length() >
0)
+ sb.append("&RelayState=").append(urlEncodedRelayState);
+ return sb.toString();
+
+ }
+ return null;
+ }
+
+ public ResponseType getErrorResponse(String responseURL, String status,
+ String identityURL) throws ServletException
+ {
+ if(redirectProfile)
+ {
+ try
+ {
+ ResponseType responseType = null;
+
+ SAML2Response saml2Response = new SAML2Response();
+
+ //Create a response type
+ String id = IDGenerator.create("ID_");
+
+ IssuerInfoHolder issuerHolder = new IssuerInfoHolder(identityURL);
+ issuerHolder.setStatusCode(status);
+
+ IDPInfoHolder idp = new IDPInfoHolder();
+ idp.setNameIDFormatValue(null);
+ idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+
+ SPInfoHolder sp = new SPInfoHolder();
+ sp.setResponseDestinationURI(responseURL);
+ responseType = saml2Response.createResponseType(id, sp, idp, issuerHolder);
+
+ log.debug("ResponseType = ");
+ //Lets see how the response looks like
+ if(log.isTraceEnabled())
+ {
+ StringWriter sw = new StringWriter();
+ saml2Response.marshall(responseType, sw);
+ log.trace("Response="+sw.toString());
+ }
+
+ return responseType;
+ }
+ catch(Exception e)
+ {
+ log.error("Exception in getErrorResponse::",e);
+ throw new ServletException(e.getLocalizedMessage());
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
Added:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPPostFormAuthenticator.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPPostFormAuthenticator.java
(rev 0)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/tomcat/sp/SPPostFormAuthenticator.java 2009-05-22
22:38:21 UTC (rev 512)
@@ -0,0 +1,273 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.jboss.identity.federation.bindings.tomcat.sp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.xml.bind.JAXBException;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.authenticator.Constants;
+import org.apache.catalina.authenticator.FormAuthenticator;
+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.log4j.Logger;
+import org.jboss.identity.federation.api.saml.v2.request.SAML2Request;
+import org.jboss.identity.federation.api.saml.v2.response.SAML2Response;
+import org.jboss.identity.federation.bindings.config.SPType;
+import org.jboss.identity.federation.bindings.config.TrustType;
+import
org.jboss.identity.federation.bindings.tomcat.sp.holder.ServiceProviderSAMLContext;
+import org.jboss.identity.federation.bindings.util.PostBindingUtil;
+import org.jboss.identity.federation.bindings.util.ValveUtil;
+import org.jboss.identity.federation.core.saml.v2.exceptions.AssertionExpiredException;
+import org.jboss.identity.federation.core.saml.v2.exceptions.IssuerNotTrustedException;
+import org.jboss.identity.federation.saml.v2.assertion.EncryptedElementType;
+import org.jboss.identity.federation.saml.v2.protocol.AuthnRequestType;
+import org.jboss.identity.federation.saml.v2.protocol.ResponseType;
+import org.xml.sax.SAXException;
+
+/**
+ * Authenticator at the Service Provider
+ * that handles HTTP/Post binding of SAML 2
+ * but falls back on Form Authentication
+ *
+ * @author Anil.Saldhana(a)redhat.com
+ * @since Dec 12, 2008
+ */
+public class SPPostFormAuthenticator extends FormAuthenticator
+{
+ private static Logger log = Logger.getLogger(SPPostFormAuthenticator.class);
+
+ protected SPType spConfiguration = null;
+
+ private String serviceURL = null;
+ private String identityURL = null;
+
+ public SPPostFormAuthenticator()
+ {
+ super();
+ }
+
+ @Override
+ public void start() throws LifecycleException
+ {
+ String configFile = "/WEB-INF/jboss-idfed.xml";
+ super.start();
+
+ InputStream is = context.getServletContext().getResourceAsStream(configFile);
+ if(is == null)
+ throw new RuntimeException(configFile + " missing");
+ try
+ {
+ spConfiguration = ValveUtil.getSPConfiguration(is);
+ this.identityURL = spConfiguration.getIdentityURL();
+ this.serviceURL = spConfiguration.getServiceURL();
+ log.trace("Identity Provider URL=" + this.identityURL);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean authenticate(Request request, Response response, LoginConfig
loginConfig) throws IOException
+ {
+ SPUtil spUtil = new SPUtil();
+
+ Principal principal = request.getUserPrincipal();
+ if (principal != null)
+ {
+ log.debug("Already authenticated '" + principal.getName() +
"'");
+ return true;
+ }
+
+ Session session = request.getSessionInternal(true);
+ String relayState = request.getParameter("RelayState");
+
+ //Try to get the username
+ try
+ {
+ principal = (GenericPrincipal) process(request,response);
+
+ if(principal == null)
+ {
+ AuthnRequestType authnRequest = spUtil.createSAMLRequest(serviceURL,
identityURL);
+ sendRequestToIDP(authnRequest, relayState, response);
+ return false;
+ }
+
+ String username = principal.getName();
+ String password = ServiceProviderSAMLContext.EMPTY_PASSWORD;
+
+ //Map to JBoss specific principal
+ if(spConfiguration.getServerEnvironment().equalsIgnoreCase("JBOSS"))
+ {
+ GenericPrincipal gp = (GenericPrincipal) principal;
+ //Push a context
+ ServiceProviderSAMLContext.push(username, Arrays.asList(gp.getRoles()));
+ principal = context.getRealm().authenticate(username, password);
+ ServiceProviderSAMLContext.clear();
+ }
+
+ session.setNote(Constants.SESS_USERNAME_NOTE, username);
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ request.setUserPrincipal(principal);
+ register(request, response, principal, Constants.FORM_METHOD, username,
password);
+
+ return true;
+ }
+ catch(AssertionExpiredException aie)
+ {
+ log.debug("Assertion has expired. Issuing a new saml2 request to the
IDP");
+ try
+ {
+ AuthnRequestType authnRequest = spUtil.createSAMLRequest(serviceURL,
identityURL);
+ sendRequestToIDP(authnRequest, relayState, response);
+ }
+ catch (Exception e)
+ {
+ log.trace("Exception:",e);
+ }
+ return false;
+ }
+ catch(Exception e)
+ {
+ log.debug("Exception :",e);
+ }
+
+ //fallback
+ return super.authenticate(request, response, loginConfig);
+ }
+
+ protected void sendRequestToIDP(AuthnRequestType authnRequest, String relayState,
Response response)
+ throws IOException, SAXException, JAXBException
+ {
+ SAML2Request saml2Request = new SAML2Request();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ saml2Request.marshall(authnRequest, baos);
+
+ String samlMessage = PostBindingUtil.base64Encode(baos.toString());
+ String destination = authnRequest.getDestination();
+ PostBindingUtil.sendPost(destination, samlMessage, relayState, response, true);
+ }
+
+ protected AuthnRequestType createSAMLRequestMessage(String relayState, Response
response)
+ throws Exception
+ {
+ //create a saml request
+ if(this.serviceURL == null)
+ throw new ServletException("serviceURL is not configured");
+
+ SPUtil spUtil = new SPUtil();
+ return spUtil.createSAMLRequest(serviceURL, identityURL);
+
+ }
+
+ protected String getDestination(String urlEncodedRequest, String
urlEncodedRelayState)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("?SAMLRequest=").append(urlEncodedRequest);
+ if(urlEncodedRelayState != null && urlEncodedRelayState.length() > 0)
+ sb.append("&RelayState=").append(urlEncodedRelayState);
+ return sb.toString();
+ }
+
+ protected void isTrusted(String issuer) throws IssuerNotTrustedException
+ {
+ try
+ {
+ String issuerDomain = ValveUtil.getDomain(issuer);
+ TrustType idpTrust = spConfiguration.getTrust();
+ if(idpTrust != null)
+ {
+ String domainsTrusted = idpTrust.getDomains();
+ if(domainsTrusted.indexOf(issuerDomain) < 0)
+ throw new IssuerNotTrustedException(issuer);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IssuerNotTrustedException(e.getLocalizedMessage(),e);
+ }
+ }
+
+ protected boolean validate(Request request) throws Exception
+ {
+ return request.getParameter("SAMLResponse") != null;
+ }
+
+ /**
+ * Subclasses should provide the implementation
+ * @param responseType ResponseType that contains the encrypted assertion
+ * @return response type with the decrypted assertion
+ */
+ protected ResponseType decryptAssertion(ResponseType responseType) throws Exception
+ {
+ throw new RuntimeException("This authenticator does not handle
encryption");
+ }
+
+ private Principal process(Request request, Response response) throws Exception
+ {
+ Principal userPrincipal = null;
+
+ String samlResponse = request.getParameter("SAMLResponse");
+ if(samlResponse != null && samlResponse.length() > 0 )
+ {
+ this.validate(request);
+
+ //deal with SAML response from IDP
+ byte[] base64DecodedResponse = PostBindingUtil.base64Decode(samlResponse);
+ InputStream is = new ByteArrayInputStream(base64DecodedResponse);
+
+ SAML2Response saml2Response = new SAML2Response();
+
+ ResponseType responseType = saml2Response.getResponseType(is);
+
+ this.isTrusted(responseType.getIssuer().getValue());
+
+ List<Object> assertions =
responseType.getAssertionOrEncryptedAssertion();
+ if(assertions.size() == 0)
+ throw new IllegalStateException("No assertions in reply from IDP");
+
+ Object assertion = assertions.get(0);
+ if(assertion instanceof EncryptedElementType)
+ {
+ responseType = this.decryptAssertion(responseType);
+ }
+
+ SPUtil spUtil = new SPUtil();
+ return spUtil.handleSAMLResponse(request, responseType);
+ }
+ return userPrincipal;
+ }
+}
\ No newline at end of file
Added:
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/util/PostBindingUtil.java
===================================================================
---
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/util/PostBindingUtil.java
(rev 0)
+++
identity-federation/trunk/jboss-identity-bindings/src/main/java/org/jboss/identity/federation/bindings/util/PostBindingUtil.java 2009-05-22
22:38:21 UTC (rev 512)
@@ -0,0 +1,106 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, 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.jboss.identity.federation.bindings.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.jboss.identity.federation.api.util.Base64;
+
+/**
+ * Utility for the HTTP/Post binding
+ * @author Anil.Saldhana(a)redhat.com
+ * @since May 22, 2009
+ */
+public class PostBindingUtil
+{
+ private static Logger log = Logger.getLogger(PostBindingUtil.class);
+
+
+ public static String base64Encode(String stringToEncode) throws IOException
+ {
+ return Base64.encodeBytes(stringToEncode.getBytes("UTF-8"),
Base64.DONT_BREAK_LINES);
+ }
+
+ public static byte[] base64Decode(String encodedString)
+ {
+ return Base64.decode(encodedString);
+ }
+
+ /**
+ * Send the response to the redirected destination while
+ * adding the character encoding of "UTF-8" as well as
+ * adding headers for cache-control and Pragma
+ * @param destination Destination URI where the response needs to redirect
+ * @param response HttpServletResponse
+ * @throws IOException
+ */
+ public static void sendPost(String destination,
+ String samlMessage, String relayState,
+ HttpServletResponse response,
+ boolean sendToIDP)
+ throws IOException
+ {
+ String key = sendToIDP ? "SAMLRequest" : "SAMLResponse";
+
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ common(destination, response);
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("<HTML>");
+ builder.append("<HEAD>");
+ if(sendToIDP)
+ builder.append("<TITLE>HTTP Post Binding To Identity
Provider</TITLE>");
+ else
+ builder.append("<TITLE>HTTP Post Binding Response To Service
Provider</TITLE>");
+
+ builder.append("</HEAD>");
+ builder.append("<BODY
Onload=\"document.forms[0].submit()\">");
+
+ builder.append("<FORM METHOD=\"POST\" ACTION=\"" +
destination + "\">");
+ builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\""+ key
+"\"" + " VALUE=\"" + samlMessage
+ + "\"/>");
+ if (relayState != null && relayState.length() > 0)
+ {
+ builder.append("<INPUT TYPE=\"HIDDEN\"
NAME=\"RelayState\" " +
+ "VALUE=\"" + relayState + "\"/>");
+ }
+ //builder.append("<INPUT TYPE=\"submit\"
VALUE=\"Continue\"/>");
+ builder.append("</FORM></BODY></HTML>");
+
+ String str = builder.toString();
+ log.debug(str);
+ out.println(str);
+ out.close();
+ }
+
+ private static void common(String destination, HttpServletResponse response)
+ {
+ response.setCharacterEncoding("UTF-8");
+ response.setHeader("Pragma", "no-cache");
+ response.setHeader("Cache-Control", "no-cache, no-store");
+ }
+}
\ No newline at end of file