test.SAMLAttributeQueryExample.java Source code

Java tutorial

Introduction

Here is the source code for test.SAMLAttributeQueryExample.java

Source

/*
 * 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 test;

import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.annotation.Nonnull;
import javax.net.ssl.SSLContext;

import net.shibboleth.utilities.java.support.security.Type4UuidIdentifierGenerationStrategy;
import net.shibboleth.utilities.java.support.xml.BasicParserPool;
import net.shibboleth.utilities.java.support.xml.SerializeSupport;

import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.cryptacular.util.CertUtil;
import org.cryptacular.util.KeyPairUtil;
import org.joda.time.DateTime;
import org.opensaml.core.config.InitializationService;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.XMLObjectBuilderFactory;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.messaging.context.InOutOperationContext;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObjectBuilder;
import org.opensaml.saml.common.SAMLVersion;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeQuery;
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.StatusCode;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.soap.client.http.HttpSOAPClient;
import org.opensaml.soap.messaging.context.SOAP11Context;
import org.opensaml.soap.soap11.Body;
import org.opensaml.soap.soap11.Envelope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Send an attribute query to an IdP.
 */
public class SAMLAttributeQueryExample {

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

    /** Constructor. */
    public SAMLAttributeQueryExample() {
    }

    /**
     * Send an attribute query to an IdP.
     * 
     * @param args program arguments
     * @throws Exception if an error occurs
     */
    public static void main(String[] args) throws Exception {
        SAMLAttributeQueryExample attributeQueryExample = new SAMLAttributeQueryExample();
        attributeQueryExample.sendAttributeQuery();
    }

    /**
     * Send an attribute query to an IdP.
     * 
     * @throws Exception if an error occurs
     */
    public void sendAttributeQuery() throws Exception {

        String endpoint = "https://idp.example.org:8443/idp/profile/SAML2/SOAP/AttributeQuery";

        String requester = "https://sp.example.org/shibboleth";

        String idpCertificateFile = "/opt/shib/idp/credentials/idp.crt";
        String clientTLSPrivateKeyFile = "/opt/local/etc/shibboleth/sp-key.pem";
        String clientTLSCertificateFile = "/opt/local/etc/shibboleth/sp-cert.pem";

        String principalName = "jdoe";
        String expectedAttributeFriendlyName = "mail";
        String expectedAttributeValue = "jdoe@example.org";

        InitializationService.initialize();

        AttributeQuery attributeQuery = buildAttributeQueryRequest(requester, principalName);

        Envelope envelope = buildSOAP11Envelope(attributeQuery);

        HttpClient httpClient = buildHttpClient(idpCertificateFile, clientTLSPrivateKeyFile,
                clientTLSCertificateFile);

        BasicParserPool parserPool = new BasicParserPool();
        parserPool.initialize();

        HttpSOAPClient httpSoapClient = new HttpSOAPClient(httpClient, parserPool);

        InOutOperationContext context = buildInOutOperationContext(envelope);

        httpSoapClient.send(endpoint, context);

        Envelope soapResponse = context.getInboundMessageContext().getSubcontext(SOAP11Context.class).getEnvelope();
        System.out.println("SOAP Response was:");
        System.out.println(SerializeSupport.prettyPrintXML(soapResponse.getDOM()));

        // Verify the response was a success and the expected attribute was returned.
        if (verifyResponse(soapResponse, principalName, expectedAttributeFriendlyName, expectedAttributeValue)) {
            System.out.println("Response completed successfully.");
        } else {
            System.err.println("Response not completed successfully.");
        }
    }

    /**
     * Builds a basic attribute query.
     * 
     * @param requester the requester
     * @param principalName the principal name
     * @return the attribute query
     */
    @Nonnull
    public static AttributeQuery buildAttributeQueryRequest(@Nonnull final String requester,
            @Nonnull final String principalName) {
        XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
        final SAMLObjectBuilder<Issuer> issuerBuilder = (SAMLObjectBuilder<Issuer>) bf
                .<Issuer>getBuilderOrThrow(Issuer.DEFAULT_ELEMENT_NAME);
        final Issuer issuer = issuerBuilder.buildObject();
        issuer.setValue(requester);

        final SAMLObjectBuilder<NameID> nameIdBuilder = (SAMLObjectBuilder<NameID>) bf
                .<NameID>getBuilderOrThrow(NameID.DEFAULT_ELEMENT_NAME);
        final NameID nameId = nameIdBuilder.buildObject();
        nameId.setValue(principalName);
        nameId.setFormat(NameID.PERSISTENT);

        final SAMLObjectBuilder<Subject> subjectBuilder = (SAMLObjectBuilder<Subject>) bf
                .<Subject>getBuilderOrThrow(Subject.DEFAULT_ELEMENT_NAME);
        final Subject subject = subjectBuilder.buildObject();
        subject.setNameID(nameId);

        final SAMLObjectBuilder<AttributeQuery> queryBuilder = (SAMLObjectBuilder<AttributeQuery>) bf
                .<AttributeQuery>getBuilderOrThrow(AttributeQuery.DEFAULT_ELEMENT_NAME);
        final AttributeQuery query = queryBuilder.buildObject();
        query.setID(new Type4UuidIdentifierGenerationStrategy().generateIdentifier());
        query.setIssueInstant(new DateTime());
        query.setIssuer(issuer);
        query.setSubject(subject);
        query.setVersion(SAMLVersion.VERSION_20);

        return query;
    }

    /**
     * Build the envelope.
     * 
     * @param payload the payload
     * @return the envelope
     */
    public static Envelope buildSOAP11Envelope(XMLObject payload) {
        XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
        Envelope envelope = (Envelope) bf.getBuilder(Envelope.DEFAULT_ELEMENT_NAME)
                .buildObject(Envelope.DEFAULT_ELEMENT_NAME);
        Body body = (Body) bf.getBuilder(Body.DEFAULT_ELEMENT_NAME).buildObject(Body.DEFAULT_ELEMENT_NAME);

        body.getUnknownXMLObjects().add(payload);
        envelope.setBody(body);

        return envelope;
    }

    /**
     * Build the HTTP client.
     * 
     * @param idpCertificateFile path to idp certificate file
     * @param clientPrivateKeyFile path to client private key file
     * @param clientCertificateFile path to client certificate file
     * @return the HTTP client
     * @throws Exception if an error occurs
     */
    @Nonnull
    public static HttpClient buildHttpClient(@Nonnull final String idpCertificateFile,
            @Nonnull final String clientPrivateKeyFile, @Nonnull final String clientCertificateFile)
            throws Exception {

        X509Certificate idpCert = CertUtil.readCertificate(idpCertificateFile);
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);
        trustStore.setCertificateEntry("idp", idpCert);

        PrivateKey clientPrivateKey = KeyPairUtil.readPrivateKey(clientPrivateKeyFile);
        X509Certificate clientCert = CertUtil.readCertificate(clientCertificateFile);
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);
        keyStore.setKeyEntry("me", clientPrivateKey, "secret".toCharArray(), new Certificate[] { clientCert });

        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
        sslContextBuilder.loadTrustMaterial(trustStore);
        sslContextBuilder.loadKeyMaterial(keyStore, "secret".toCharArray());
        SSLContext sslcontext = sslContextBuilder.build();

        CloseableHttpClient httpClient = HttpClients.custom().setSslcontext(sslcontext).build();

        return httpClient;
    }

    /**
     * Build the {@link InOutOperationContext}.
     * 
     * @param envelope the envelope
     * @return the context
     */
    @Nonnull
    public static InOutOperationContext buildInOutOperationContext(@Nonnull final Envelope envelope) {
        SOAP11Context soap11Ctx = new SOAP11Context();
        soap11Ctx.setEnvelope(envelope);

        MessageContext msgCtx = new MessageContext();
        msgCtx.addSubcontext(soap11Ctx);

        InOutOperationContext inOutOpCtx = new InOutOperationContext() {
        };
        inOutOpCtx.setOutboundMessageContext(msgCtx);

        return inOutOpCtx;
    }

    /**
     * Verify that response was a success and the expected attribute was returned.
     * 
     * @param soapResponse the response
     * @param principal the principal
     * @param expectedAttributeFriendlyName the expected attribute name
     * @param expectedAttributeValue the expected attribute value
     * @return whether or not the response was a success and the expected attribute was returned
     */
    public static boolean verifyResponse(Envelope soapResponse, String principal,
            String expectedAttributeFriendlyName, String expectedAttributeValue) {
        //
        Response response = (Response) soapResponse.getBody().getUnknownXMLObjects().get(0);
        if (!response.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) {
            System.err.println("Response was not a success.");
            return false;
        }

        Assertion assertion = response.getAssertions().get(0);

        if (!assertion.getSubject().getNameID().getValue().equals(principal)) {
            System.err.println("Subject does not match.");
            return false;
        }

        boolean expectedAttributeWasReturned = false;
        for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) {
            for (Attribute attribute : attributeStatement.getAttributes()) {
                for (XMLObject value : attribute.getAttributeValues()) {
                    if (attribute.getFriendlyName().equals(expectedAttributeFriendlyName)) {
                        if (((XSString) value).getValue().equals(expectedAttributeValue)) {
                            expectedAttributeWasReturned = true;
                        }
                    }
                }
            }
        }

        if (!expectedAttributeWasReturned) {
            System.err.println("Response did not contain the expected attribute.");
            return false;
        }

        return true;
    }
}