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

org.opensaml.saml1.binding.encoding.BaseSAML1MessageEncoder Maven / Gradle / Ivy

Go to download

The OpenSAML-J library provides tools to support developers working with the Security Assertion Markup Language (SAML).

There is a newer version: 2.6.4
Show newest version
/*
 * Licensed to the University Corporation for Advanced Internet Development, 
 * Inc. (UCAID) under one or more contributor license agreements.  See the 
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID licenses this file to You 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 org.opensaml.saml1.binding.encoding;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.opensaml.Configuration;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.SignableSAMLObject;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
import org.opensaml.saml1.core.Response;
import org.opensaml.saml2.metadata.Endpoint;
import org.opensaml.util.URLBuilder;
import org.opensaml.ws.message.encoder.BaseMessageEncoder;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.SignatureException;
import org.opensaml.xml.signature.Signer;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base class for SAML 1 message encoders.
 */
public abstract class BaseSAML1MessageEncoder extends BaseMessageEncoder implements SAMLMessageEncoder{

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(BaseSAML1MessageEncoder.class);
    
    /** The list of schemes allowed to appear in URLs related to the encoded message. Defaults to 'http' and 'https'. */
    private List allowedURLSchemes;

    public BaseSAML1MessageEncoder() {
        super();
        setAllowedURLSchemes(new String[] { "http", "https" });
    }

    /**
     * Gets the unmodifiable list of schemes allowed to appear in URLs related to the encoded message.
     * 
     * @return list of URL schemes allowed to appear in a message
     */
    public List getAllowedURLSchemes() {
        return allowedURLSchemes;
    }

    /**
     * Sets the list of list of schemes allowed to appear in URLs related to the encoded message. Note, the appearance
     * of schemes such as 'javascript' may open the system up to attacks (e.g. cross-site scripting attacks).
     * 
     * @param schemes URL schemes allowed to appear in a message
     */
    public void setAllowedURLSchemes(String[] schemes) {
        if (schemes == null || schemes.length == 0) {
            allowedURLSchemes = Collections.emptyList();
        } else {
            List temp = new ArrayList();
            for (String scheme : schemes) {
                temp.add(scheme);
            }
            allowedURLSchemes = Collections.unmodifiableList(temp);
        }
    }

    /**
     * Gets the response URL from the relying party endpoint. If the SAML message is a {@link Response} and the relying
     * party endpoint contains a response location then that location is returned otherwise the normal endpoint location
     * is returned.
     * 
     * @param messageContext current message context
     * 
     * @return response URL from the relying party endpoint
     * 
     * @throws MessageEncodingException throw if no relying party endpoint is available
     */
    protected URLBuilder getEndpointURL(SAMLMessageContext messageContext) throws MessageEncodingException {
        Endpoint endpoint = messageContext.getPeerEntityEndpoint();
        if (endpoint == null) {
            throw new MessageEncodingException("Endpoint for relying party was null.");
        }

        URLBuilder urlBuilder;
        if (messageContext.getOutboundSAMLMessage() instanceof Response
                && !DatatypeHelper.isEmpty(endpoint.getResponseLocation())) {
            urlBuilder = new URLBuilder(endpoint.getResponseLocation());
        } else {
            if (DatatypeHelper.isEmpty(endpoint.getLocation())) {
                throw new MessageEncodingException("Relying party endpoint location was null or empty.");
            }
            urlBuilder = new URLBuilder(endpoint.getLocation());
        }
        
        if(!getAllowedURLSchemes().contains(urlBuilder.getScheme())){
           throw new MessageEncodingException("Relying party endpoint used the untrusted URL scheme " + urlBuilder.getScheme()); 
        }
        return urlBuilder;
    }

    /**
     * Signs the given SAML message if it a {@link SignableSAMLObject} and this encoder has signing credentials.
     * 
     * @param messageContext current message context
     * 
     * @throws MessageEncodingException thrown if there is a problem preparing the signature for signing
     */
    @SuppressWarnings("unchecked")
    protected void signMessage(SAMLMessageContext messageContext) throws MessageEncodingException {
        SAMLObject outboundMessage = messageContext.getOutboundSAMLMessage();
        if (outboundMessage instanceof SignableSAMLObject
                && messageContext.getOuboundSAMLMessageSigningCredential() != null) {
            log.debug("Signing outbound SAML message.");
            SignableSAMLObject signableMessage = (SignableSAMLObject) outboundMessage;
            Credential signingCredential = messageContext.getOuboundSAMLMessageSigningCredential();

            XMLObjectBuilder signatureBuilder = Configuration.getBuilderFactory().getBuilder(
                    Signature.DEFAULT_ELEMENT_NAME);
            Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
            signature.setSigningCredential(signingCredential);

            try {
                // TODO pull SecurityConfiguration from SAMLMessageContext? needs to be added
                // TODO pull binding-specific keyInfoGenName from encoder setting, etc?
                SecurityHelper.prepareSignatureParams(signature, signingCredential, null, null);
            } catch (SecurityException e) {
                throw new MessageEncodingException("Error preparing signature for signing", e);
            }

            signableMessage.setSignature(signature);

            try {
                Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(signableMessage);
                marshaller.marshall(signableMessage);
                Signer.signObject(signature);
            } catch (MarshallingException e) {
                log.error("Unable to marshall protocol message in preparation for signing", e);
                throw new MessageEncodingException("Unable to marshall protocol message in preparation for signing", e);
            } catch (SignatureException e) {
                log.error("Unable to sign protocol message", e);
                throw new MessageEncodingException("Unable to sign protocol message", e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy