org.guanxi.sp.engine.service.saml2.SAML2ProfileService.java Source code

Java tutorial

Introduction

Here is the source code for org.guanxi.sp.engine.service.saml2.SAML2ProfileService.java

Source

//: "The contents of this file are subject to the Mozilla Public License
//: Version 1.1 (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.mozilla.org/MPL/
//:
//: Software distributed under the License is distributed on an "AS IS"
//: basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//: License for the specific language governing rights and limitations
//: under the License.
//:
//: The Original Code is Guanxi (http://www.guanxi.uhi.ac.uk).
//:
//: The Initial Developer of the Original Code is Alistair Young alistair@codebrane.com
//: All Rights Reserved.
//:

package org.guanxi.sp.engine.service.saml2;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlOptions;
import org.guanxi.common.GuanxiException;
import org.guanxi.common.Utils;
import org.guanxi.common.definitions.SAML;
import org.guanxi.common.entity.EntityFarm;
import org.guanxi.common.entity.EntityManager;
import org.guanxi.common.metadata.Metadata;
import org.guanxi.common.security.SecUtilsConfig;
import org.guanxi.sp.engine.service.generic.ProfileService;
import org.guanxi.xal.saml2.metadata.GuardRoleDescriptorExtensions;
import org.guanxi.xal.saml_2_0.assertion.NameIDType;
import org.guanxi.xal.saml_2_0.metadata.EndpointType;
import org.guanxi.xal.saml_2_0.metadata.EntityDescriptorType;
import org.guanxi.xal.saml_2_0.protocol.AuthnRequestDocument;
import org.guanxi.xal.saml_2_0.protocol.AuthnRequestType;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.HashMap;

/**
 * SAML2 ProfileService implementation
 *
 * @author alistair
 */
public class SAML2ProfileService implements ProfileService {
    /** Our logger */
    private static final Logger logger = Logger.getLogger(SAML2ProfileService.class.getName());
    /** The JSP to use to POST the AuthnRequest to the IdP */
    private String httpPOSTView = null;
    /** The JSP to use to GET the AuthnRequest to the IdP */
    private String httpRedirectView = null;
    /** The default endpoint for receiving SAML Response messages */
    private String assertionConsumerServiceURL = null;

    /** @see org.guanxi.sp.engine.service.generic.ProfileService#init() */
    public void init() {
    }

    /** @see org.guanxi.sp.engine.service.generic.ProfileService#doProfile(javax.servlet.http.HttpServletRequest, String, String, org.guanxi.xal.saml2.metadata.GuardRoleDescriptorExtensions, String, org.guanxi.common.entity.EntityFarm) */
    public ModelAndView doProfile(HttpServletRequest request, String guardID, String guardSessionID,
            GuardRoleDescriptorExtensions guardNativeMetadata, String entityID, EntityFarm farm)
            throws GuanxiException {
        ModelAndView mAndV = new ModelAndView();

        String relayState = guardSessionID.replaceAll("GUARD", "ENGINE");

        // Load the metadata for the IdP
        EntityManager manager = farm.getEntityManagerForID(entityID);
        if (manager == null) {
            logger.error("Could not find manager for IdP '" + entityID);
            throw new GuanxiException("Could not find manager for IdP " + entityID);
        }
        Metadata entityMetadata = manager.getMetadata(entityID);
        if (entityMetadata == null) {
            logger.error("Could not find manager for IdP " + entityID);
            throw new GuanxiException("Could not find metadata for IdP " + entityID);
        }
        EntityDescriptorType saml2Metadata = (EntityDescriptorType) entityMetadata.getPrivateData();

        String wbssoURL = null;
        String binding = null;
        EndpointType[] ssos = saml2Metadata.getIDPSSODescriptorArray(0).getSingleSignOnServiceArray();
        for (EndpointType sso : ssos) {
            if ((sso.getBinding().equalsIgnoreCase(SAML.SAML2_BINDING_HTTP_POST))
                    || (sso.getBinding().equalsIgnoreCase(SAML.SAML2_BINDING_HTTP_REDIRECT))) {
                wbssoURL = sso.getLocation();
                if (sso.getBinding().equalsIgnoreCase(SAML.SAML2_BINDING_HTTP_POST))
                    binding = SAML.SAML2_BINDING_HTTP_POST;
                else if (sso.getBinding().equalsIgnoreCase(SAML.SAML2_BINDING_HTTP_REDIRECT))
                    binding = SAML.SAML2_BINDING_HTTP_REDIRECT;
                break;
            }
        }
        if (wbssoURL == null) {
            logger.error("IdP does not support WBSSO " + entityID);
            throw new GuanxiException("IdP does not support WBSSO " + entityID);
        }

        // Create an AuthnRequest
        AuthnRequestDocument authnRequestDoc = AuthnRequestDocument.Factory.newInstance();
        AuthnRequestType authnRequest = authnRequestDoc.addNewAuthnRequest();
        authnRequest.setID(Utils.createNCNameID());
        authnRequest.setVersion("2.0");
        authnRequest.setIssueInstant(Calendar.getInstance());
        Utils.zuluXmlObject(authnRequest, 0);
        NameIDType issuer = NameIDType.Factory.newInstance();
        issuer.setStringValue(guardID);
        authnRequest.setIssuer(issuer);
        authnRequest.setAssertionConsumerServiceURL(assertionConsumerServiceURL);
        authnRequest.setProtocolBinding(SAML.SAML2_BINDING_HTTP_POST);
        // Only if signed
        //authnRequest.setDestination("https://sgarbh.smo.uhi.ac.uk:8443/idp/profile/SAML2/POST/SSO");

        // Sort out the namespaces for saving the Response
        HashMap<String, String> namespaces = new HashMap<String, String>();
        namespaces.put(SAML.NS_SAML_20_PROTOCOL, SAML.NS_PREFIX_SAML_20_PROTOCOL);
        namespaces.put(SAML.NS_SAML_20_ASSERTION, SAML.NS_PREFIX_SAML_20_ASSERTION);

        XmlOptions xmlOptions = new XmlOptions();
        xmlOptions.setSavePrettyPrint();
        xmlOptions.setSavePrettyPrintIndent(2);
        xmlOptions.setUseDefaultNamespace();
        xmlOptions.setSaveAggressiveNamespaces();
        xmlOptions.setSaveSuggestedPrefixes(namespaces);
        xmlOptions.setSaveNamespacesFirst();

        // Get the config ready for signing
        SecUtilsConfig secUtilsConfig = new SecUtilsConfig();
        secUtilsConfig.setKeystoreFile(guardNativeMetadata.getKeystore());
        secUtilsConfig.setKeystorePass(guardNativeMetadata.getKeystorePassword());
        secUtilsConfig.setKeystoreType("JKS");
        secUtilsConfig.setPrivateKeyAlias(guardID);
        secUtilsConfig.setPrivateKeyPass(guardNativeMetadata.getKeystorePassword());
        secUtilsConfig.setCertificateAlias(guardID);

        // Break out to DOM land to get the SAML Response signed...
        /*
        Document signedDoc = null;
        try {
          // Need to use newDomNode to preserve namespace information
          signedDoc = SecUtils.getInstance().sign(secUtilsConfig, (Document)authnRequestDoc.newDomNode(xmlOptions), "");
          // ...and go back to XMLBeans land when it's ready
          authnRequestDoc = AuthnRequestDocument.Factory.parse(signedDoc);
        }
        catch(GuanxiException ge) {
          logger.error("Could not sign AuthnRequest", ge);
          mAndV.setViewName(errorView);
          mAndV.getModel().put(errorViewDisplayVar, messages.getMessage("engine.error.could.not.sign.message",
                                                                null, request.getLocale()));
          return mAndV;
        }
        catch(XmlException xe) {
          logger.error("Couldn't convert signed AuthnRequest back to XMLBeans", xe);
          mAndV.setViewName(errorView);
          mAndV.getModel().put(errorViewDisplayVar, messages.getMessage("engine.error.could.not.sign.message",
                                                                null, request.getLocale()));
          return mAndV;
        }
        */

        // Base 64 encode the AuthnRequest
        //String authnRequestB64 = Utils.base64(signedDoc);
        //String authnRequestB64 = Utils.base64((Document)authnRequestDoc.newDomNode(xmlOptions));

        // Do the profile quickstep
        String authnRequestForIdP = null;
        if (binding.equals(SAML.SAML2_BINDING_HTTP_REDIRECT)) {
            mAndV.setViewName(httpRedirectView);
            String deflatedRequest = Utils.deflate(authnRequestDoc.toString(),
                    Utils.RFC1951_DEFAULT_COMPRESSION_LEVEL, Utils.RFC1951_NO_WRAP);
            authnRequestForIdP = Utils.base64(deflatedRequest.getBytes());
            authnRequestForIdP = authnRequestForIdP.replaceAll(System.getProperty("line.separator"), "");
            try {
                authnRequestForIdP = URLEncoder.encode(authnRequestForIdP, "UTF-8");
                relayState = URLEncoder.encode(relayState, "UTF-8");
            } catch (UnsupportedEncodingException uee) {
                logger.error("couldn't encode SAMLRequest");
                throw new GuanxiException("couldn't encode SAMLRequest: " + uee.getMessage());
            }
        } else if (binding.equals(SAML.SAML2_BINDING_HTTP_POST)) {
            mAndV.setViewName(httpPOSTView);
            authnRequestForIdP = Utils.base64(authnRequestDoc.toString().getBytes());
        }

        // Send the AuthnRequest to the IdP
        mAndV.getModel().put("SAMLRequest", authnRequestForIdP);
        mAndV.getModel().put("RelayState", relayState);
        mAndV.getModel().put("wbsso_endpoint", wbssoURL);
        return mAndV;
    }

    // Setters
    public void setHttpPOSTView(String httpPOSTView) {
        this.httpPOSTView = httpPOSTView;
    }

    public void setHttpRedirectView(String httpRedirectView) {
        this.httpRedirectView = httpRedirectView;
    }

    public void setAssertionConsumerServiceURL(String assertionConsumerServiceURL) {
        this.assertionConsumerServiceURL = assertionConsumerServiceURL;
    }
}