be.fedict.eid.idp.protocol.ws_federation.AbstractWSFederationMetadataHttpServlet.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.eid.idp.protocol.ws_federation.AbstractWSFederationMetadataHttpServlet.java

Source

/*
 * eID Identity Provider Project.
 * Copyright (C) 2010 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * 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, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.idp.protocol.ws_federation;

import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBException;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.KeyDescriptor;
import org.opensaml.saml2.metadata.RoleDescriptor;
import org.opensaml.ws.wsaddressing.Address;
import org.opensaml.ws.wsaddressing.EndpointReference;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.security.keyinfo.KeyInfoHelper;
import org.w3c.dom.Element;

import be.fedict.eid.idp.common.saml2.Saml2Util;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimType;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypeBuilder;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypeMarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypeUnmarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypesOffered;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypesOfferedBuilder;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypesOfferedMarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.ClaimTypesOfferedUnmarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.Description;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DescriptionBuilder;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DescriptionMarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DescriptionUnmarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DisplayName;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DisplayNameBuilder;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DisplayNameMarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.DisplayNameUnmarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.PassiveRequestorEndpoint;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.PassiveRequestorEndpointBuilder;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.PassiveRequestorEndpointMarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.PassiveRequestorEndpointUnmarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.SecurityTokenService;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.SecurityTokenServiceBuilder;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.SecurityTokenServiceMarshaller;
import be.fedict.eid.idp.protocol.ws_federation.wsfed.SecurityTokenServiceUnmarshaller;
import be.fedict.eid.idp.spi.AttributeConfig;
import be.fedict.eid.idp.spi.IdPIdentity;
import be.fedict.eid.idp.spi.IdentityProviderConfiguration;
import be.fedict.eid.idp.spi.IdentityProviderConfigurationFactory;
import be.fedict.eid.idp.spi.IdentityProviderProtocolService;

public abstract class AbstractWSFederationMetadataHttpServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static final Log LOG = LogFactory.getLog(AbstractWSFederationMetadataHttpServlet.class);

    static {
        /*
         * Next is because Sun loves to endorse crippled versions of Xerces.
         */
        System.setProperty("javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema",
                "org.apache.xerces.jaxp.validation.XMLSchemaFactory");
        try {
            DefaultBootstrap.bootstrap();

            // register WS-Federation Metadata elements
            Configuration.registerObjectProvider(ClaimType.DEFAULT_ELEMENT_NAME, new ClaimTypeBuilder(),
                    new ClaimTypeMarshaller(), new ClaimTypeUnmarshaller());

            Configuration.registerObjectProvider(ClaimTypesOffered.DEFAULT_ELEMENT_NAME,
                    new ClaimTypesOfferedBuilder(), new ClaimTypesOfferedMarshaller(),
                    new ClaimTypesOfferedUnmarshaller());

            Configuration.registerObjectProvider(Description.DEFAULT_ELEMENT_NAME, new DescriptionBuilder(),
                    new DescriptionMarshaller(), new DescriptionUnmarshaller());

            Configuration.registerObjectProvider(DisplayName.DEFAULT_ELEMENT_NAME, new DisplayNameBuilder(),
                    new DisplayNameMarshaller(), new DisplayNameUnmarshaller());

            Configuration.registerObjectProvider(PassiveRequestorEndpoint.DEFAULT_ELEMENT_NAME,
                    new PassiveRequestorEndpointBuilder(), new PassiveRequestorEndpointMarshaller(),
                    new PassiveRequestorEndpointUnmarshaller());

            Configuration.registerObjectProvider(SecurityTokenService.DEFAULT_ELEMENT_NAME,
                    new SecurityTokenServiceBuilder(), new SecurityTokenServiceMarshaller(),
                    new SecurityTokenServiceUnmarshaller());

            Configuration.registerObjectProvider(SecurityTokenService.TYPE_NAME, new SecurityTokenServiceBuilder(),
                    new SecurityTokenServiceMarshaller(), new SecurityTokenServiceUnmarshaller());

        } catch (ConfigurationException e) {
            throw new RuntimeException("could not bootstrap the OpenSAML2 library", e);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        LOG.debug("doGet");
        response.setContentType("application/samlmetadata+xml");

        IdentityProviderConfiguration configuration = IdentityProviderConfigurationFactory.getInstance(request);

        OutputStream outputStream = response.getOutputStream();
        try {
            writeMetadata(request, configuration, outputStream);
        } catch (Exception e) {
            throw new ServletException("error: " + e.getMessage(), e);
        }
    }

    private void writeMetadata(HttpServletRequest request, IdentityProviderConfiguration configuration,
            OutputStream outputStream) throws JAXBException, ServletException, ParserConfigurationException,
            CertificateEncodingException, TransformerFactoryConfigurationError, TransformerException, IOException,
            NoSuchAlgorithmException, InvalidAlgorithmParameterException, MarshalException, XMLSignatureException {

        String location = getLocation(request);

        EntityDescriptor entityDescriptor = Saml2Util.buildXMLObject(EntityDescriptor.class,
                EntityDescriptor.DEFAULT_ELEMENT_NAME);
        entityDescriptor.setEntityID(location);
        entityDescriptor.setID("saml-metadata-" + UUID.randomUUID().toString());

        @SuppressWarnings("unchecked")
        XMLObjectBuilder<SecurityTokenService> builder = Configuration.getBuilderFactory()
                .getBuilder(SecurityTokenService.TYPE_NAME);
        SecurityTokenService securityTokenService = builder.buildObject(RoleDescriptor.DEFAULT_ELEMENT_NAME,
                SecurityTokenService.TYPE_NAME);
        entityDescriptor.getRoleDescriptors().add(securityTokenService);

        securityTokenService.addSupportedProtocol("http://docs.oasis-open.org/wsfed/federation/200706");

        PassiveRequestorEndpoint passiveRequestorEndpoint = Saml2Util.buildXMLObject(PassiveRequestorEndpoint.class,
                PassiveRequestorEndpoint.DEFAULT_ELEMENT_NAME);
        securityTokenService.getPassiveRequestorEndpoints().add(passiveRequestorEndpoint);

        EndpointReference endpoint = Saml2Util.buildXMLObject(EndpointReference.class,
                EndpointReference.ELEMENT_NAME);
        passiveRequestorEndpoint.setEndpointReference(endpoint);

        Address address = Saml2Util.buildXMLObject(Address.class, Address.ELEMENT_NAME);
        endpoint.setAddress(address);
        address.setValue(location);

        IdPIdentity identity = configuration.findIdentity();
        try {
            if (null != identity) {

                KeyDescriptor keyDescriptor = Saml2Util.buildXMLObject(KeyDescriptor.class,
                        KeyDescriptor.DEFAULT_ELEMENT_NAME);
                securityTokenService.getKeyDescriptors().add(keyDescriptor);
                keyDescriptor.setUse(UsageType.SIGNING);

                org.opensaml.xml.signature.KeyInfo keyInfo = Saml2Util.buildXMLObject(
                        org.opensaml.xml.signature.KeyInfo.class,
                        org.opensaml.xml.signature.KeyInfo.DEFAULT_ELEMENT_NAME);
                keyDescriptor.setKeyInfo(keyInfo);

                KeyInfoHelper.addCertificate(keyInfo,
                        (X509Certificate) identity.getPrivateKeyEntry().getCertificate());
            }
        } catch (CertificateEncodingException e) {
            throw new RuntimeException("opensaml2 certificate encoding error: " + e.getMessage(), e);
        }

        // claims
        ClaimTypesOffered claimTypesOffered = Saml2Util.buildXMLObject(ClaimTypesOffered.class,
                ClaimTypesOffered.DEFAULT_ELEMENT_NAME);
        securityTokenService.setClaimTypesOffered(claimTypesOffered);

        List<ClaimType> claimTypes = claimTypesOffered.getClaimTypes();

        for (AttributeConfig attribute : configuration
                .getAttributes(AbstractWSFederationProtocolService.WS_FED_PROTOCOL_ID)) {
            addClaimType(attribute.getUri(), attribute.getName(), attribute.getDescription(), claimTypes);
        }

        Element element;
        if (null != identity) {

            LOG.debug("sign WS-Federation Metadata");
            element = Saml2Util.signAsElement(entityDescriptor, entityDescriptor, identity.getPrivateKeyEntry());
        } else {

            LOG.warn("WS-Federation Metadata NOT signed!");
            element = Saml2Util.marshall(entityDescriptor);
        }

        Saml2Util.writeDocument(element.getOwnerDocument(), outputStream);
    }

    private void addClaimType(String claimUri, String displayName, String description, List<ClaimType> claimTypes) {

        ClaimType claim = Saml2Util.buildXMLObject(ClaimType.class, ClaimType.DEFAULT_ELEMENT_NAME);
        claimTypes.add(claim);
        claim.setUri(claimUri);
        claim.setOptional(true);

        DisplayName claimDisplayName = Saml2Util.buildXMLObject(DisplayName.class,
                DisplayName.DEFAULT_ELEMENT_NAME);
        claimDisplayName.setValue(displayName);
        claim.setDisplayName(claimDisplayName);

        Description claimDescription = Saml2Util.buildXMLObject(Description.class,
                Description.DEFAULT_ELEMENT_NAME);
        claimDescription.setValue(description);
        claim.setDescription(claimDescription);
    }

    private String getLocation(HttpServletRequest request) {

        String location = "https://" + request.getServerName();
        if (request.getServerPort() != 443) {
            location += ":" + request.getServerPort();
        }
        location += request.getContextPath() + IdentityProviderProtocolService.PROTOCOL_ENDPOINT_PATH + "/"
                + getPath();
        LOG.debug("location: " + location);
        return location;
    }

    protected abstract String getPath();
}