com.vmware.identity.saml.impl.TokenValidatorImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.identity.saml.impl.TokenValidatorImpl.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.saml.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.lang.Validate;

import com.vmware.identity.diagnostics.DiagnosticsLoggerFactory;
import com.vmware.identity.diagnostics.IDiagnosticsLogger;
import com.vmware.identity.idm.PrincipalId;
import com.vmware.identity.saml.InvalidPrincipalException;
import com.vmware.identity.saml.InvalidSignatureException;
import com.vmware.identity.saml.InvalidTokenException;
import com.vmware.identity.saml.PrincipalAttribute;
import com.vmware.identity.saml.PrincipalAttributeDefinition;
import com.vmware.identity.saml.PrincipalAttributesExtractor;
import com.vmware.identity.saml.ServerValidatableSamlToken;
import com.vmware.identity.saml.ServerValidatableSamlToken.SubjectValidation;
import com.vmware.identity.saml.SystemException;
import com.vmware.identity.saml.TokenValidator;

/**
 * This class validates that already issued SAML token is valid as of now.
 * (assertion statements: all statements within the assertion are validated)
 */
public final class TokenValidatorImpl implements TokenValidator {

    private static final IDiagnosticsLogger logger = DiagnosticsLoggerFactory.getLogger(TokenValidatorImpl.class);
    private static final String GROUP = "http://rsa.com/schemas/attr-names/2009/01/GroupIdentity";

    private final Collection<PrincipalAttributeDefinition> supportedAttributes;
    private final PrincipalAttributesExtractor principalAttributesExtractor;

    private final TokenValidator authnTokenValidator;

    /**
     * @param authnTokenValidator
     *        token validator performing authn only token validation. not {@code null}.
     * @param principalAttributesExtractor
     *           required
     */
    public TokenValidatorImpl(TokenValidator authnTokenValidator,
            PrincipalAttributesExtractor principalAttributesExtractor) {
        Validate.notNull(authnTokenValidator);
        Validate.notNull(principalAttributesExtractor);

        this.authnTokenValidator = authnTokenValidator;
        this.principalAttributesExtractor = principalAttributesExtractor;
        this.supportedAttributes = principalAttributesExtractor.getAllAttributeDefinitions();
    }

    @Override
    public ServerValidatableSamlToken validate(ServerValidatableSamlToken token)
            throws InvalidSignatureException, InvalidTokenException, SystemException {

        logger.debug("Validating token.");
        final ServerValidatableSamlToken result = this.authnTokenValidator.validate(token);
        logger.debug("Token validated");
        logger.debug(String.format("Token is from external idp: [%s]", result.isExternal()));

        if (result.isExternal() == true) {
            logger.warn("Cannot perform online validation for token from external idp.");
            throw new InvalidTokenException(
                    "External tokens cannot be on-line validated. They should be validtade through their issuer.");
        }
        if ((result.getSubject().subjectValidation() != SubjectValidation.Regular)
                || (result.getSubject().subjectUpn() == null)) {
            logger.warn("Token's subject is invalid. [SubjectValidation={}]",
                    result.getSubject().subjectValidation());
            throw new InvalidTokenException("Token's subject is invalid.");
        }

        // TODO [866694][849937] make this check on all attributes which are
        // parameterized
        // TODO check [866704] for details
        checkGroupList(token.getGroupList(), token.getSubject().subjectUpn());
        logger.debug("Group list is verified.");

        // TODO [866694] check for other attributes (first, last name,
        // isSolution)?

        logger.info("Token {} for principal {} successfully validated.", token.getId(),
                token.getSubject().subjectUpn());

        return result;
    }

    /**
     * Checks if the group list from the token is equal to the current group list
     * for the given principal
     *
     * @param groupList
     *           required
     * @param subject
     *           required
     * @throws InvalidTokenException
     *            if the token group list differs from the current group list
     */
    private void checkGroupList(List<PrincipalId> groupList, PrincipalId subject)
            throws InvalidTokenException, SystemException {

        // TODO pass attributes for which validation should be performed
        PrincipalAttributeDefinition groupAttributeDefinition = getSupportedAttributeDefinition(GROUP);
        if (groupAttributeDefinition == null) {
            throw new InvalidTokenException(GROUP + " is not supported attribute in token attributes.");
        }

        final PrincipalAttribute groupAttribute;
        try {
            groupAttribute = getAttribute(subject, groupAttributeDefinition);
        } catch (InvalidPrincipalException e) {
            throw new InvalidTokenException(String.format(
                    "Principal %s not found, race condition suspected since the principal has just been validated.",
                    subject), e);
        }

        List<PrincipalId> currentGroupList = toGroupList(groupAttribute.getValues());
        List<PrincipalId> tokenGroupList = groupList;
        if (tokenGroupList.size() != currentGroupList.size() || !tokenGroupList.containsAll(currentGroupList)) {
            throw new InvalidTokenException(
                    "Current group membership of " + "the principal is different from the one stated in the token");
        }

    }

    private PrincipalAttribute getAttribute(PrincipalId subject, PrincipalAttributeDefinition attributeDefinition)
            throws InvalidPrincipalException, SystemException {
        assert subject != null;
        assert attributeDefinition != null;

        Collection<PrincipalAttributeDefinition> attributeDefinitions = new HashSet<PrincipalAttributeDefinition>();
        attributeDefinitions.add(attributeDefinition);
        final Set<PrincipalAttribute> attributes = principalAttributesExtractor.getAttributes(subject,
                attributeDefinitions);
        if (attributes == null || attributes.size() != 1) {
            throw new IllegalStateException(String.format("No single attribute found for %s searching for %s",
                    subject, attributeDefinition));
        }

        PrincipalAttribute attribute = attributes.iterator().next();
        assert attribute != null;
        return attribute;
    }

    /**
     * @param attributeName
     *           not null, name of attribute
     * @return attribute definition of the attribute with given name or null if
     *         this attribute is not supported
     */
    private PrincipalAttributeDefinition getSupportedAttributeDefinition(String attributeName) {
        assert attributeName != null;

        PrincipalAttributeDefinition result = null;
        for (PrincipalAttributeDefinition attr : supportedAttributes) {
            if (attr.getName().equalsIgnoreCase(attributeName)) {
                result = attr;
                break;
            }
        }

        return result;
    }

    private PrincipalId parseGroupId(String groupId) {
        Validate.notEmpty(groupId, "groupId");

        final PrincipalId group;
        if (groupId.contains("\\")) {
            String[] parts = splitInTwo(groupId, '\\');
            group = new PrincipalId(parts[1], parts[0]);

        } else {
            String[] parts = splitInTwo(groupId, '/');
            group = new PrincipalId(parts[0], parts[1]);
        }

        return group;
    }

    /**
     * @param value
     *           required
     * @param separator
     *           required
     * @return
     */
    private String[] splitInTwo(String value, char separator) {

        Pattern splitter = Pattern.compile(Pattern.quote(String.valueOf(separator)));
        String split[] = splitter.split(value, 3);
        if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) {
            throw new IllegalStateException(
                    String.format("Invalid principal value: `%s' (incorrect number of fields)", value));
        }

        return split;
    }

    /**
     * @param groups
     *           can be null
     * @return the converted list or empty list
     */
    private List<PrincipalId> toGroupList(String[] groups) {
        List<PrincipalId> result = new ArrayList<PrincipalId>();
        if (groups != null) {
            for (String groupId : groups) {
                result.add(parseGroupId(groupId));
            }
        }
        return result;
    }
}