All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cloudfoundry.identity.uaa.authentication.SamlAssertionDecoder Maven / Gradle / Ivy

The newest version!
/*
 * ****************************************************************************
 *     Cloud Foundry
 *     Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
 *
 *     This product is licensed to you under the Apache License, Version 2.0 (the "License").
 *     You may not use this product except in compliance with the License.
 *
 *     This product includes a number of subcomponents with
 *     separate copyright notices and license terms. Your use of these
 *     subcomponents is subject to the terms and conditions of the
 *     subcomponent's license, as noted in the LICENSE file.
 * ****************************************************************************
 */

package org.cloudfoundry.identity.uaa.authentication;

import org.cloudfoundry.identity.uaa.provider.saml.SamlRedirectUtils;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Response;
import org.opensaml.ws.message.MessageContext;
import org.opensaml.ws.message.decoder.MessageDecodingException;
import org.opensaml.ws.transport.http.HTTPInTransport;
import org.opensaml.xml.parse.ParserPool;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * Copy/paste from org.opensaml.saml2.binding.decoding.HTTPPostDecoder
 * with two minor changes
 * 1. base64 decoding is doing base64url decoding
 * 2. The unmarshalling of the object gets wrapped in a SamlResponse object
 */

public class SamlAssertionDecoder extends BaseSAML2MessageDecoder {

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(SamlAssertionDecoder.class);

    /** Constructor. */
    public SamlAssertionDecoder() {
        super();
    }

    /**
     * Constructor.
     *
     * @param pool parser pool used to deserialize messages
     */
    public SamlAssertionDecoder(ParserPool pool) {
        super(pool);
    }

    /** {@inheritDoc} */
    public String getBindingURI() {
        return "urn:oasis:names:tc:SAML:2.0:bindings:URI";
    }

    /** {@inheritDoc} */
    protected boolean isIntendedDestinationEndpointURIRequired(SAMLMessageContext samlMsgCtx) {
        return isMessageSigned(samlMsgCtx);
    }

    /** {@inheritDoc} */
    protected void doDecode(MessageContext messageContext) throws MessageDecodingException {
        if (!(messageContext instanceof SAMLMessageContext)) {
            log.error("Invalid message context type, this decoder only support SAMLMessageContext");
            throw new MessageDecodingException(
                "Invalid message context type, this decoder only support SAMLMessageContext");
        }

        if (!(messageContext.getInboundMessageTransport() instanceof HTTPInTransport)) {
            log.error("Invalid inbound message transport type, this decoder only support HTTPInTransport");
            throw new MessageDecodingException(
                "Invalid inbound message transport type, this decoder only support HTTPInTransport");
        }

        SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;

        HTTPInTransport inTransport = (HTTPInTransport) samlMsgCtx.getInboundMessageTransport();
        if (!inTransport.getHTTPMethod().equalsIgnoreCase("POST")) {
            throw new MessageDecodingException("This message decoder only supports the HTTP POST method");
        }

        String relayState = inTransport.getParameterValue("RelayState");
        samlMsgCtx.setRelayState(relayState);
        log.debug("Decoded SAML relay state of: {}", relayState);

        InputStream base64DecodedMessage = getBase64DecodedMessage(inTransport);
        Assertion inboundMessage = (Assertion) unmarshallMessage(base64DecodedMessage);
        Response response = SamlRedirectUtils.wrapAssertionIntoResponse(inboundMessage, inboundMessage.getIssuer().getValue());
        samlMsgCtx.setInboundMessage(response);
        samlMsgCtx.setInboundSAMLMessage(response);
        log.debug("Decoded SAML message");

        populateMessageContext(samlMsgCtx);
    }

    /**
     * Gets the Base64 encoded message from the request and decodes it.
     *
     * @param transport inbound message transport
     *
     * @return decoded message
     *
     * @throws MessageDecodingException thrown if the message does not contain a base64 encoded SAML message
     */
    protected InputStream getBase64DecodedMessage(HTTPInTransport transport) throws MessageDecodingException {
        log.debug("Getting Base64 encoded message from request");
        String encodedMessage = transport.getParameterValue("assertion");


        if (DatatypeHelper.isEmpty(encodedMessage)) {
            log.error("Request did not contain either a SAMLRequest or "
                          + "SAMLResponse paramter.  Invalid request for SAML 2 HTTP POST binding.");
            throw new MessageDecodingException("No SAML message present in request");
        }

        log.trace("Base64 decoding SAML message:\n{}", encodedMessage);
        byte[] decodedBytes = org.apache.commons.codec.binary.Base64.decodeBase64(encodedMessage.getBytes(StandardCharsets.UTF_8));
        if(decodedBytes == null){
            log.error("Unable to Base64 decode SAML message");
            throw new MessageDecodingException("Unable to Base64 decode SAML message");
        }

        log.trace("Decoded SAML message:\n{}", new String(decodedBytes));
        return new ByteArrayInputStream(decodedBytes);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy