com.vmware.identity.wstrust.client.impl.AcquireTokenByGssResponseHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.identity.wstrust.client.impl.AcquireTokenByGssResponseHandler.java

Source

/*
 *  Copyright (c) 2012-2015 VMware, Inc.  All Rights Reserved.
 *
 *   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.vmware.identity.wstrust.client.impl;

import javax.xml.bind.JAXBContext;

import org.apache.commons.codec.binary.Base64;
import org.oasis_open.docs.ws_sx.ws_trust._200512.BinaryExchangeType;
import org.oasis_open.docs.ws_sx.ws_trust._200512.RequestSecurityTokenResponseCollectionType;
import org.oasis_open.docs.ws_sx.ws_trust._200512.RequestSecurityTokenResponseType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.vmware.vim.sso.client.exception.InvalidTokenException;
import com.vmware.vim.sso.client.exception.MalformedTokenException;
import com.vmware.identity.wstrust.client.SsoRequestException;

/**
 * AcquireTokenByGssResponseHandler implementation of the
 * {@link ResponseUnmarshaller}. It can parse GSS related responses.
 */
final class AcquireTokenByGssResponseHandler implements ResponseHandler<GssResult> {

    private final ResponseUnmarshaller<Element> responseUnmarshaller;
    private static final String PROCESS_RSTR_ERROR = "Error processing the response to SPNego token request";
    private static final String RSTRC_ELEMENT_NAME = "RequestSecurityTokenResponseCollection";
    private static final String RSTRC_NAMESPACE = "http://docs.oasis-open.org/ws-sx/ws-trust/200512";

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

    public AcquireTokenByGssResponseHandler(JAXBContext context) throws SsoRequestException {
        this.responseUnmarshaller = new ResponseUnmarshaller<>(context);
    }

    @Override
    public GssResult parseResponse(Node response) throws ParserException, InvalidTokenException {

        Element returnedToken = findReturnedToken(response);
        RequestSecurityTokenResponseType parsedResponse = returnedToken == null
                ? parseIntermediateGssResponse(response)
                : parseFinalGssResponse(response);

        byte[] leg = extractBinaryExchangeData(parsedResponse.getBinaryExchange());

        GssResult parseResult = new GssResult(leg, returnedToken, parsedResponse.getContext());

        return parseResult;
    }

    /**
     * Helper: find, extract and serialize the SAML Token (i.e. <saml:Assertion>
     * element) under the given DOM Node.
     *
     * @param node
     *            the root of the DOM tree to search for a SAML token.
     * @return the serialized form of the token found, if any. If no token is
     *         found, returns {@code null}. If more than one token is found, a
     *         ParserException is thrown.
     */
    private Element findReturnedToken(Node node) throws ParserException {
        NodeList assertionNodes = ((Element) node).getElementsByTagNameNS(ResponseUnmarshaller.TOKEN_TYPE_SAML2,
                ResponseUnmarshaller.ASSERTION_ELEMENT_NAME);

        if (assertionNodes.getLength() == 0) {
            return null;
        }

        if (assertionNodes.getLength() > 1) {
            log.debug(PROCESS_RSTR_ERROR + ": more than one saml:Assertion returned");
            throw new ParserException(PROCESS_RSTR_ERROR);
        }

        return (Element) assertionNodes.item(0);
    }

    /**
     * Helper: Whether response contains an RSTRC
     *
     * @param node
     *            the root of the DOM tree to search for TSTRC.
     * @return true if rstrc is founf false otherwise.
     */
    private boolean hasRSTRC(Node node) throws ParserException {

        boolean found = false;
        if ((RSTRC_ELEMENT_NAME.equalsIgnoreCase(node.getLocalName()))
                && RSTRC_NAMESPACE.equalsIgnoreCase(node.getNamespaceURI())) {
            log.debug("Node is the RSTRC.");
            found = true;
        } else {
            NodeList rstrcNodes = ((Element) node).getElementsByTagNameNS(RSTRC_NAMESPACE, RSTRC_ELEMENT_NAME);

            log.debug(
                    String.format("Found %d RSTRC elements.", ((rstrcNodes == null) ? 0 : rstrcNodes.getLength())));
            found = (rstrcNodes.getLength() > 0);
        }

        return found;
    }

    /**
     * Helper: parse to a JAXB object an intermediate (i.e. not containing a
     * Token) response to acquire-token-by-GSS-negotiation request.
     *
     * @throws ParserException
     *             if the response is not valid.
     */
    private RequestSecurityTokenResponseType parseIntermediateGssResponse(Node response) throws ParserException {

        RequestSecurityTokenResponseType parsedResponse = null;
        boolean hasRSTRC = hasRSTRC(response);

        // parse the response, but don't validate
        if (hasRSTRC) {
            log.debug("Intermediate response contains RSTRC. Parsing RSTR from there.");
            RequestSecurityTokenResponseCollectionType rstrc = responseUnmarshaller.parseStsResponse(response,
                    RequestSecurityTokenResponseCollectionType.class, true);
            parsedResponse = rstrc.getRequestSecurityTokenResponse();
        } else {
            log.debug("Intermediate response does not contain RSTRC. Should be direct RSTR.");
            parsedResponse = responseUnmarshaller.parseStsResponse(response, RequestSecurityTokenResponseType.class,
                    true);
        }

        if (parsedResponse == null) {
            log.error("Unable to get RSTR element from response.");
            throw new ParserException(PROCESS_RSTR_ERROR);
        }

        validateContextId(parsedResponse);

        return parsedResponse;
    }

    /**
     * Helper: parse to a JAXB object the final (i.e. the one containing a
     * token) response to acquire-token-by-GSS-negotiation request.
     *
     * @throws ParserException
     *             if the response is not valid.
     * @throws MalformedTokenException
     *             if the response's structure by itself is valid, but the
     *             contained token is not.
     */
    private RequestSecurityTokenResponseType parseFinalGssResponse(Node response)
            throws ParserException, MalformedTokenException {

        RequestSecurityTokenResponseType parsedResponse = responseUnmarshaller.parseStsResponse(response);

        new SamlTokenValidator().validateTokenType(parsedResponse);

        validateContextId(parsedResponse);

        return parsedResponse;
    }

    /**
     * Helper: validate the given BinaryExchange and extract it's data.
     *
     * @param element
     *            the JAXB object representation of a <wst:BinaryExchange>
     *            element or <code>null</code> when we are on the last
     *            negotiation step when using NTLM protocol.
     * @return the data in byte[] format (i.e. after base64 decoding) or
     *         <code>null</code> when the passed binary exchange parameter was
     *         <code>null</code>
     *
     * @throws ParserException
     *             if the BinaryExchange is not valid.
     */
    private byte[] extractBinaryExchangeData(BinaryExchangeType element) throws ParserException {

        if (element == null) {
            return null;
        }

        if (!Constants.ENCODING_TYPE_BASE64.equals(element.getEncodingType())
                || !Constants.BINARY_EXCHANGE_TYPE_SPNEGO.equals(element.getValueType())) {

            log.debug(PROCESS_RSTR_ERROR);
            throw new ParserException(PROCESS_RSTR_ERROR);
        }

        return Base64.decodeBase64(element.getValue());
    }

    /**
     * Helper: validate the Context attribute of the given
     * RequestSecurityTokenResponse (and throw ParseException if it is not
     * valid).
     */
    private void validateContextId(RequestSecurityTokenResponseType response) throws ParserException {

        String contextId = response.getContext();
        if (contextId == null) {
            log.debug(PROCESS_RSTR_ERROR + ": Context is null");
            throw new ParserException(PROCESS_RSTR_ERROR);
        }
    }
}