ddf.security.cas.WebSSOTokenValidator.java Source code

Java tutorial

Introduction

Here is the source code for ddf.security.cas.WebSSOTokenValidator.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p>
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.security.cas;

import java.nio.charset.Charset;
import java.util.Base64;

import javax.security.auth.callback.CallbackHandler;

import org.apache.commons.lang.StringUtils;
import org.apache.cxf.sts.STSPropertiesMBean;
import org.apache.cxf.sts.request.ReceivedToken;
import org.apache.cxf.sts.request.ReceivedToken.STATE;
import org.apache.cxf.sts.token.validator.TokenValidator;
import org.apache.cxf.sts.token.validator.TokenValidatorParameters;
import org.apache.cxf.sts.token.validator.TokenValidatorResponse;
import org.apache.cxf.ws.security.sts.provider.model.secext.BinarySecurityTokenType;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.dom.engine.WSSConfig;
import org.apache.wss4j.dom.handler.RequestData;
import org.codice.ddf.configuration.PropertyResolver;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ddf.security.encryption.EncryptionService;

/**
 * Validates Web Single Sign-On Tokens.
 *
 * @author kcwire
 */
public class WebSSOTokenValidator implements TokenValidator {

    // The Supported SSO Token Types
    public static final String CAS_TYPE = "#CAS";

    public static final String CAS_BST_SEP = "|";

    private static final Logger LOGGER = LoggerFactory.getLogger(WebSSOTokenValidator.class);

    private PropertyResolver casServerUrl;

    private EncryptionService encryptionService;

    public String getCasServerUrl() {
        return casServerUrl.getResolvedString();
    }

    public void setCasServerUrl(String casServerUrl) {
        this.casServerUrl = new PropertyResolver(casServerUrl);
    }

    public void setEncryptionService(EncryptionService encryptionService) {
        this.encryptionService = encryptionService;
    }

    /*
     * Return true if this TokenValidator implementation is capable of validating the ReceivedToken
     * argument.
     */
    @Override
    public boolean canHandleToken(ReceivedToken validateTarget) {
        return canHandleToken(validateTarget, null);
    }

    /*
     * Return true if this TokenValidator implementation is capable of validating the ReceivedToken
     * argument. The realm is ignored in this token Validator.
     */
    @Override
    public boolean canHandleToken(ReceivedToken validateTarget, String realm) {
        final Object token = validateTarget.getToken();
        // Check the ValueType to see if this is a supported SSO Token.
        if ((token instanceof BinarySecurityTokenType)) {
            if (CAS_TYPE.equalsIgnoreCase(((BinarySecurityTokenType) token).getValueType())) {
                LOGGER.debug("Can handle token type of: " + ((BinarySecurityTokenType) token).getValueType());
                return true;
            }
            LOGGER.debug("Cannot handle token type of: " + ((BinarySecurityTokenType) token).getValueType());
        }
        return false;
    }

    /**
     * Validate a Token using the given TokenValidatorParameters.
     */
    @Override
    public TokenValidatorResponse validateToken(TokenValidatorParameters tokenParameters) {
        LOGGER.debug("Validating SSO Token");
        STSPropertiesMBean stsProperties = tokenParameters.getStsProperties();
        Crypto sigCrypto = stsProperties.getSignatureCrypto();
        CallbackHandler callbackHandler = stsProperties.getCallbackHandler();

        RequestData requestData = new RequestData();
        requestData.setSigVerCrypto(sigCrypto);
        WSSConfig wssConfig = WSSConfig.getNewInstance();
        requestData.setWssConfig(wssConfig);
        requestData.setCallbackHandler(callbackHandler);

        LOGGER.debug("Setting validate state to invalid before check.");
        TokenValidatorResponse response = new TokenValidatorResponse();
        ReceivedToken validateTarget = tokenParameters.getToken();
        validateTarget.setState(STATE.INVALID);
        response.setToken(validateTarget);

        if (!validateTarget.isBinarySecurityToken()) {
            LOGGER.debug("Validate target is not a binary security token, returning invalid response.");
            return response;
        }
        LOGGER.debug("Getting binary security token from validate target");
        BinarySecurityTokenType binarySecurityToken = (BinarySecurityTokenType) validateTarget.getToken();

        //
        // Decode the token
        //
        LOGGER.debug("Decoding binary security token.");
        String base64Token = binarySecurityToken.getValue();
        String ticket = null;
        String service = null;
        try {
            byte[] token = Base64.getDecoder().decode(base64Token);
            if (token == null || token.length == 0) {
                throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
                        "Binary security token NOT successfully decoded, is empty or null.");
            }
            String decodedToken = new String(token, Charset.forName("UTF-8"));
            if (StringUtils.isNotBlank(decodedToken)) {
                LOGGER.debug("Binary security token successfully decoded: {}", decodedToken);
                // Token is in the format ticket|service
                String[] parts = StringUtils.split(decodedToken, CAS_BST_SEP);
                if (parts.length == 2) {
                    ticket = parts[0];
                    service = parts[1];
                } else {
                    throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
                            "Was not able to parse out BST propertly. Should be in ticket|service format.");
                }
            } else {
                throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
                        "Binary security token NOT successfully decoded, is empty or null.");
            }
        } catch (WSSecurityException wsse) {
            String msg = "Unable to decode BST into ticket and service for validation to CAS.";
            LOGGER.error(msg, wsse);
            return response;
        }

        //
        // Do some validation of the token here
        //
        try {
            LOGGER.debug("Validating ticket [{}] for service [{}].", ticket, service);

            // validate either returns an assertion or throws an exception
            Assertion assertion = validate(ticket, service);

            AttributePrincipal principal = assertion.getPrincipal();
            LOGGER.debug("User name retrieved from CAS: {}", principal.getName());

            response.setPrincipal(principal);
            LOGGER.debug("CAS ticket successfully validated, setting state to valid.");
            validateTarget.setState(STATE.VALID);

        } catch (TicketValidationException e) {
            LOGGER.error("Unable to validate CAS token.", e);
        }

        return response;
    }

    /**
     * Validate the CAS ticket and service
     *
     * @param ticket
     * @param service
     * @return
     * @throws TicketValidationException
     */
    public Assertion validate(String ticket, String service) throws TicketValidationException {
        LOGGER.trace("CAS Server URL = " + casServerUrl);

        Cas20ProxyTicketValidator casValidator = new Cas20ProxyTicketValidator(casServerUrl.getResolvedString());
        casValidator.setAcceptAnyProxy(true);

        return casValidator.validate(ticket, service);
    }

}