nl.surfnet.mujina.spring.SAMLAuthenticationEntryPoint.java Source code

Java tutorial

Introduction

Here is the source code for nl.surfnet.mujina.spring.SAMLAuthenticationEntryPoint.java

Source

/*
 * Copyright 2012 SURFnet bv, The Netherlands
 *
 * 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 nl.surfnet.mujina.spring;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.Validate;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.metadata.Endpoint;
import org.opensaml.saml2.metadata.SingleSignOnService;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.xml.security.CriteriaSet;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.credential.CredentialResolver;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.security.criteria.EntityIDCriteria;
import org.opensaml.xml.security.criteria.UsageCriteria;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.StringUtils;

import nl.surfnet.mujina.model.SpConfiguration;
import nl.surfnet.mujina.saml.AuthnRequestGenerator;
import nl.surfnet.mujina.saml.xml.EndpointGenerator;
import nl.surfnet.mujina.util.IDService;
import nl.surfnet.mujina.util.TimeService;
import nl.surfnet.spring.security.opensaml.SAMLMessageHandler;

public class SAMLAuthenticationEntryPoint implements AuthenticationEntryPoint {

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

    private final TimeService timeService;
    private final IDService idService;

    private SAMLMessageHandler bindingAdapter;
    private CredentialResolver credentialResolver;

    private SpConfiguration spConfiguration;

    public SAMLAuthenticationEntryPoint(TimeService timeService, IDService idService) {
        super();
        this.timeService = timeService;
        this.idService = idService;
    }

    @Required
    public void setBindingAdapter(SAMLMessageHandler bindingAdapter) {
        this.bindingAdapter = bindingAdapter;
    }

    @Required
    public void setCredentialResolver(CredentialResolver credentialResolver) {
        this.credentialResolver = credentialResolver;
    }

    public void setConfiguration(final SpConfiguration spConfiguration) {
        this.spConfiguration = spConfiguration;
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {

        AuthnRequestGenerator authnRequestGenerator = new AuthnRequestGenerator(spConfiguration.getEntityID(),
                timeService, idService);
        EndpointGenerator endpointGenerator = new EndpointGenerator();

        String singleSignOnServiceURL = spConfiguration.getSingleSignOnServiceURL();

        singleSignOnServiceURL = transparentProxying(request, singleSignOnServiceURL);

        String localAssertionConsumerServiceURL = spConfiguration.getAssertionConsumerServiceURL();

        Endpoint endpoint = endpointGenerator.generateEndpoint(SingleSignOnService.DEFAULT_ELEMENT_NAME,
                singleSignOnServiceURL, localAssertionConsumerServiceURL);

        AuthnRequest authnReqeust = authnRequestGenerator.generateAuthnRequest(singleSignOnServiceURL,
                localAssertionConsumerServiceURL, spConfiguration.getProtocolBinding());

        log.debug("Sending authnRequest to {}", singleSignOnServiceURL);

        try {
            CriteriaSet criteriaSet = new CriteriaSet();
            criteriaSet.add(new EntityIDCriteria(spConfiguration.getEntityID()));
            criteriaSet.add(new UsageCriteria(UsageType.SIGNING));

            Credential signingCredential = credentialResolver.resolveSingle(criteriaSet);
            Validate.notNull(signingCredential);

            String relayState = null; // Not needed here.

            bindingAdapter.sendSAMLMessage(authnReqeust, endpoint, response, relayState, signingCredential);
        } catch (MessageEncodingException mee) {
            log.error("Could not send authnRequest to Identity Provider.", mee);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } catch (org.opensaml.xml.security.SecurityException e) {
            log.error("Unable to retrieve signing credential", e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }

    /*
     * If there is an entityID query parameter then append this as a path variable
     * MD5- hashed. We also support the transparent proxy based on a virtual
     * organization (e.g.
     * https://engine.dev.surfconext.nl/authentication/idp/single
     * -sign-on/vo:votest1/6c80081c368f0d734a48d291c76cfe1c")
     */
    private String transparentProxying(HttpServletRequest request, String singleSignOnServiceURL) {
        String entityID = request.getParameter("entityID");
        String virtualOrganization = request.getParameter("vo");
        if (StringUtils.hasText(virtualOrganization)) {
            try {
                singleSignOnServiceURL = singleSignOnServiceURL.concat("/vo:")
                        .concat(URLEncoder.encode(virtualOrganization, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        if (StringUtils.hasText(entityID)) {
            String encodeEntityID = new MessageDigestPasswordEncoder("MD5").encodePassword(entityID, null);
            singleSignOnServiceURL = singleSignOnServiceURL.concat("/").concat(encodeEntityID);
        }
        return singleSignOnServiceURL;
    }
}