com.oscgc.security.saml.idp.web.contoller.MetadataController.java Source code

Java tutorial

Introduction

Here is the source code for com.oscgc.security.saml.idp.web.contoller.MetadataController.java

Source

/* Copyright 2011 Vladimir Schafer
 *
 * 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 com.oscgc.security.saml.idp.web.contoller;

import org.opensaml.Configuration;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallerFactory;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.util.XMLHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.saml.key.JKSKeyManager;
import org.springframework.security.saml.metadata.*;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.InternalResourceView;
import org.w3c.dom.Element;

import javax.servlet.http.HttpServletRequest;
import java.security.KeyStoreException;
import java.util.*;

/**
 * Class allows manipulation of metadata from web UI.
 */
@Controller
@RequestMapping("/metadata")
public class MetadataController {

    private final Logger log = LoggerFactory.getLogger(MetadataController.class);

    public static enum AllowedSSOBindings {
        SSO_POST, SSO_PAOS, SSO_ARTIFACT, HOKSSO_POST, HOKSSO_ARTIFACT
    }

    @Autowired
    MetadataManager metadataManager;

    @Autowired
    JKSKeyManager keyManager;

    @RequestMapping
    public ModelAndView metadataList() throws MetadataProviderException {

        ModelAndView model = new ModelAndView(new InternalResourceView("/WEB-INF/security/metadataList.jsp", true));

        model.addObject("hostedSP", metadataManager.getHostedSPName());
        model.addObject("spList", metadataManager.getSPEntityNames());
        model.addObject("idpList", metadataManager.getIDPEntityNames());
        model.addObject("metadata", metadataManager.getAvailableProviders());

        return model;

    }

    @RequestMapping(value = "/refresh")
    public ModelAndView refreshMetadata() throws MetadataProviderException {
        metadataManager.refreshMetadata();
        return metadataList();

    }

    @RequestMapping(value = "/provider")
    public ModelAndView displayProvider(@RequestParam("providerIndex") int providerIndex) {

        ModelAndView model = new ModelAndView(new InternalResourceView("/WEB-INF/security/providerView.jsp", true));
        ExtendedMetadataDelegate delegate = metadataManager.getAvailableProviders().get(providerIndex);
        model.addObject("provider", delegate);
        model.addObject("providerIndex", providerIndex);
        return model;

    }

    @RequestMapping(value = "/removeProvider")
    public ModelAndView removeProvider(@RequestParam int providerIndex) throws MetadataProviderException {

        ExtendedMetadataDelegate delegate = metadataManager.getAvailableProviders().get(providerIndex);
        metadataManager.removeMetadataProvider(delegate);
        return metadataList();

    }

    @RequestMapping(value = "/generate")
    public ModelAndView generateMetadata(HttpServletRequest request) throws KeyStoreException {

        ModelAndView model = new ModelAndView(
                new InternalResourceView("/WEB-INF/security/metadataGenerator.jsp", true));
        MetadataForm defaultForm = new MetadataForm();

        model.addObject("availableKeys", getAvailablePrivateKeys());
        defaultForm.setBaseURL(getBaseURL(request));
        defaultForm.setEntityId(getEntityId(request));
        defaultForm.setAlias(getEntityId(request));
        defaultForm.setNameID(
                MetadataGenerator.defaultNameID.toArray(new String[MetadataGenerator.defaultNameID.size()])); // TODO array vs collection

        model.addObject("metadata", defaultForm);
        return model;

    }

    @RequestMapping(value = "/create")
    public ModelAndView createMetadata(@ModelAttribute("metadata") MetadataForm metadata,
            BindingResult bindingResult) throws MetadataProviderException, MarshallingException, KeyStoreException {

        new MetadataValidator(metadataManager).validate(metadata, bindingResult);

        if (bindingResult.hasErrors()) {
            ModelAndView modelAndView = new ModelAndView(
                    new InternalResourceView("/WEB-INF/security/metadataGenerator.jsp", true));
            modelAndView.addObject("availableKeys", getAvailablePrivateKeys());
            return modelAndView;
        }

        MetadataGenerator generator = new MetadataGenerator();
        generator.setKeyManager(keyManager);

        generator.setEntityId(metadata.getEntityId());
        generator.setEntityAlias(metadata.getAlias());
        generator.setEntityBaseURL(metadata.getBaseURL());
        generator.setSignMetadata(metadata.isSignMetadata());
        generator.setRequestSigned(metadata.isRequestSigned());
        generator.setWantAssertionSigned(metadata.isWantAssertionSigned());
        generator.setSigningKey(metadata.getSigningKey());
        generator.setEncryptionKey(metadata.getEncryptionKey());

        if (metadata.getTlsKey() != null && metadata.getTlsKey().length() > 0) {
            generator.setTlsKey(metadata.getTlsKey());
        }

        Collection<String> bindingsSSO = new LinkedList<String>();
        Collection<String> bindingsHoKSSO = new LinkedList<String>();
        String defaultBinding = metadata.getSsoDefaultBinding();

        int assertionConsumerIndex = 0;

        for (String binding : metadata.getSsoBindings()) {

            // Set default binding
            if (binding.equalsIgnoreCase(defaultBinding)) {
                assertionConsumerIndex = bindingsSSO.size() + bindingsHoKSSO.size();
            }

            // Set included bindings
            if (AllowedSSOBindings.SSO_POST.toString().equalsIgnoreCase(binding)) {
                bindingsSSO.add(SAMLConstants.SAML2_POST_BINDING_URI);
            } else if (AllowedSSOBindings.SSO_ARTIFACT.toString().equalsIgnoreCase(binding)) {
                bindingsSSO.add(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
            } else if (AllowedSSOBindings.SSO_PAOS.toString().equalsIgnoreCase(binding)) {
                bindingsSSO.add(SAMLConstants.SAML2_PAOS_BINDING_URI);
            } else if (AllowedSSOBindings.HOKSSO_POST.toString().equalsIgnoreCase(binding)) {
                bindingsHoKSSO.add(SAMLConstants.SAML2_POST_BINDING_URI);
            } else if (AllowedSSOBindings.HOKSSO_ARTIFACT.toString().equalsIgnoreCase(binding)) {
                bindingsHoKSSO.add(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
            }

        }

        // Set bindings
        generator.setBindingsSSO(bindingsSSO);
        generator.setBindingsHoKSSO(bindingsHoKSSO);
        generator.setAssertionConsumerIndex(assertionConsumerIndex);

        // Discovery
        if (metadata.isIncludeDiscovery()) {
            generator.setIncludeDiscovery(true);
            generator.setIncludeDiscoveryExtension(metadata.isIncludeDiscoveryExtension());
            if (metadata.getCustomDiscoveryURL() != null && metadata.getCustomDiscoveryURL().length() > 0) {
                generator.setCustomDiscoveryURL(metadata.getCustomDiscoveryURL());
            }
            if (metadata.getCustomDiscoveryResponseURL() != null
                    && metadata.getCustomDiscoveryResponseURL().length() > 0) {
                generator.setCustomDiscoveryResponseURL(metadata.getCustomDiscoveryResponseURL());
            }
        } else {
            generator.setIncludeDiscovery(false);
            generator.setIncludeDiscoveryExtension(false);
        }

        generator.setNameID(Arrays.asList(metadata.getNameID()));

        EntityDescriptor descriptor = generator.generateMetadata();
        ExtendedMetadata extendedMetadata = generator.generateExtendedMetadata();
        extendedMetadata.setSecurityProfile(metadata.getSecurityProfile());
        extendedMetadata.setSslSecurityProfile(metadata.getSslSecurityProfile());
        extendedMetadata.setRequireLogoutRequestSigned(metadata.isRequireLogoutRequestSigned());
        extendedMetadata.setRequireLogoutResponseSigned(metadata.isRequireLogoutResponseSigned());
        extendedMetadata.setRequireArtifactResolveSigned(metadata.isRequireArtifactResolveSigned());

        if (metadata.isStore()) {

            MetadataMemoryProvider memoryProvider = new MetadataMemoryProvider(descriptor);
            memoryProvider.initialize();
            MetadataProvider metadataProvider = new ExtendedMetadataDelegate(memoryProvider, extendedMetadata);
            metadataManager.addMetadataProvider(metadataProvider);
            metadataManager.setHostedSPName(descriptor.getEntityID());
            metadataManager.setRefreshRequired(true);
            metadataManager.refreshMetadata();

        }

        return displayMetadata(descriptor, extendedMetadata);

    }

    /**
     * Displays stored metadata.
     *
     * @param entityId entity ID of metadata to display
     * @return model and view
     * @throws MetadataProviderException in case metadata can't be located
     * @throws MarshallingException      in case de-serialization into string fails
     */
    @RequestMapping(value = "/display")
    public ModelAndView displayMetadata(@RequestParam("entityId") String entityId)
            throws MetadataProviderException, MarshallingException {

        EntityDescriptor entityDescriptor = metadataManager.getEntityDescriptor(entityId);
        ExtendedMetadata extendedMetadata = metadataManager.getExtendedMetadata(entityId);

        if (entityDescriptor == null) {
            throw new MetadataProviderException("Metadata with ID " + entityId + " not found");
        }

        return displayMetadata(entityDescriptor, extendedMetadata);

    }

    protected ModelAndView displayMetadata(EntityDescriptor entityDescriptor, ExtendedMetadata extendedMetadata)
            throws MarshallingException {

        MetadataForm metadata = new MetadataForm();
        String fileName = getFileName(entityDescriptor);

        metadata.setLocal(extendedMetadata.isLocal());
        metadata.setSecurityProfile(extendedMetadata.getSecurityProfile());
        metadata.setSslSecurityProfile(extendedMetadata.getSslSecurityProfile());
        metadata.setSerializedMetadata(getMetadataAsString(entityDescriptor));
        metadata.setConfiguration(getConfiguration(fileName, extendedMetadata));
        metadata.setEntityId(entityDescriptor.getEntityID());
        metadata.setAlias(extendedMetadata.getAlias());
        metadata.setRequireArtifactResolveSigned(extendedMetadata.isRequireArtifactResolveSigned());
        metadata.setRequireLogoutRequestSigned(extendedMetadata.isRequireLogoutRequestSigned());
        metadata.setRequireLogoutResponseSigned(extendedMetadata.isRequireLogoutResponseSigned());
        metadata.setEncryptionKey(extendedMetadata.getEncryptionKey());
        metadata.setSigningKey(extendedMetadata.getSigningKey());
        metadata.setTlsKey(extendedMetadata.getTlsKey());

        // TODO other fields discovery, nameIDs

        ModelAndView model = new ModelAndView(new InternalResourceView("/WEB-INF/security/metadataView.jsp", true));
        model.addObject("metadata", metadata);
        model.addObject("storagePath", fileName);

        return model;

    }

    protected String getMetadataAsString(EntityDescriptor descriptor) throws MarshallingException {

        MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory();
        Marshaller marshaller = marshallerFactory.getMarshaller(descriptor);
        Element element = marshaller.marshall(descriptor);
        return XMLHelper.nodeToString(element);

    }

    protected String getBaseURL(HttpServletRequest request) {

        StringBuffer sb = new StringBuffer();
        sb.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
                .append(request.getServerPort());
        sb.append(request.getContextPath());

        String baseURL = sb.toString();
        log.debug("Base URL {}", baseURL);
        return baseURL;

    }

    protected String getEntityId(HttpServletRequest request) {
        log.debug("Server name used as entity id {}", request.getServerName());
        return request.getServerName();
    }

    protected Map<String, String> getAvailablePrivateKeys() throws KeyStoreException {
        Map<String, String> availableKeys = new HashMap<String, String>();
        Set<String> aliases = keyManager.getAvailableCredentials();
        for (String key : aliases) {
            try {
                log.debug("Found key {}", key);
                Credential credential = keyManager.getCredential(key);
                if (credential.getPrivateKey() != null) {
                    log.debug("Adding private key with alias {} and entityID {}", key, credential.getEntityId());
                    availableKeys.put(key, key + " (" + credential.getEntityId() + ")");
                }
            } catch (Exception e) {
                log.debug("Error loading key", e);
            }
        }
        return availableKeys;
    }

    protected String getFileName(EntityDescriptor entityDescriptor) {
        StringBuilder fileName = new StringBuilder();
        for (Character c : entityDescriptor.getEntityID().toCharArray()) {
            if (Character.isJavaIdentifierPart(c)) {
                fileName.append(c);
            }
        }
        if (fileName.length() > 0) {
            fileName.append("_sp.xml");
            return fileName.toString();
        } else {
            return "default_sp.xml";
        }
    }

    protected String getConfiguration(String fileName, ExtendedMetadata metadata) {
        StringBuilder sb = new StringBuilder();
        sb.append("<bean class=\"org.springframework.security.saml.metadata.ExtendedMetadataDelegate\">\n"
                + "    <constructor-arg>\n"
                + "        <bean class=\"org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider\">\n"
                + "            <constructor-arg>\n"
                + "                <value type=\"java.io.File\">classpath:security/").append(fileName)
                .append("</value>\n" + "            </constructor-arg>\n"
                        + "            <property name=\"parserPool\" ref=\"parserPool\"/>\n" + "        </bean>\n"
                        + "    </constructor-arg>\n" + "    <constructor-arg>\n"
                        + "        <bean class=\"org.springframework.security.saml.metadata.ExtendedMetadata\">\n"
                        + "           <property name=\"local\" value=\"true\"/>\n"
                        + "           <property name=\"alias\" value=\"")
                .append(metadata.getAlias())
                .append("\"/>\n" + "           <property name=\"securityProfile\" value=\"")
                .append(metadata.getSecurityProfile())
                .append("\"/>\n" + "           <property name=\"sslSecurityProfile\" value=\"")
                .append(metadata.getSslSecurityProfile())
                .append("\"/>\n" + "           <property name=\"signingKey\" value=\"")
                .append(metadata.getSigningKey())
                .append("\"/>\n" + "           <property name=\"encryptionKey\" value=\"")
                .append(metadata.getEncryptionKey()).append("\"/>\n");
        if (metadata.getTlsKey() != null) {
            sb.append("           <property name=\"tlsKey\" value=\"").append(metadata.getTlsKey())
                    .append("\"/>\n");
        }
        sb.append("           <property name=\"requireArtifactResolveSigned\" value=\"")
                .append(metadata.isRequireArtifactResolveSigned())
                .append("\"/>\n" + "           <property name=\"requireLogoutRequestSigned\" value=\"")
                .append(metadata.isRequireLogoutRequestSigned())
                .append("\"/>\n" + "           <property name=\"requireLogoutResponseSigned\" value=\"")
                .append(metadata.isRequireLogoutResponseSigned()).append("\"/>\n");
        sb.append("           <property name=\"idpDiscoveryEnabled\" value=\"")
                .append(metadata.isIdpDiscoveryEnabled()).append("\"/>\n");
        if (metadata.isIdpDiscoveryEnabled()) {
            sb.append("           <property name=\"idpDiscoveryURL\" value=\"")
                    .append(metadata.getIdpDiscoveryURL())
                    .append("\"/>\n" + "           <property name=\"idpDiscoveryResponseURL\" value=\"")
                    .append(metadata.getIdpDiscoveryResponseURL()).append("\"/>\n");
        }
        sb.append("        </bean>\n" + "    </constructor-arg>\n" + "</bean>");
        return sb.toString();
    }

}