ddf.security.SubjectUtils.java Source code

Java tutorial

Introduction

Here is the source code for ddf.security.SubjectUtils.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;

import ddf.security.assertion.SecurityAssertion;
import ddf.security.principal.GuestPrincipal;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.x500.X500Principal;
import org.apache.commons.lang.Validate;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.opensaml.core.xml.schema.XSString;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Utility class used to perform operations on Subjects. */
public final class SubjectUtils {

    public static final String GUEST_DISPLAY_NAME = "Guest";

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

    public static final String EMAIL_ADDRESS_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";

    /** Street address */
    public static final String STREET_ADDRESS_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress";

    /** Postal address */
    public static final String POSTAL_CODE_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode";

    /** City address */
    public static final String LOCALITY_CODE_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality";

    /** Country address */
    public static final String COUNTRY_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country";

    /** Username */
    public static final String NAME_IDENTIFIER_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";

    /** Full name */
    public static final String NAME_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";

    /** First name */
    public static final String GIVEN_NAME_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname";

    /** Last name */
    public static final String SURNAME_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname";

    /** Roles */
    public static final String ROLE_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role";

    /** Mobile phone */
    public static final String MOBILE_PHONE_CLAIM_URI = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone";

    private SubjectUtils() {
    }

    /**
     * Converts the given principal name to a formatted display name.
     *
     * @param principal
     * @param defaultName
     * @return
     */
    private static String getDisplayName(Principal principal, String defaultName) {

        String displayName = defaultName;

        if (principal instanceof GuestPrincipal) {
            displayName = GUEST_DISPLAY_NAME;
        } else if (principal instanceof X500Principal) {
            displayName = getCommonName((X500Principal) principal);
        } else {
            LOGGER.debug("No display name format identified for given principal. Returning principal name ",
                    defaultName);
        }

        return displayName;
    }

    /**
     * Retrieves the user name from a given subject.
     *
     * @param subject Subject to get the user name from.
     * @return String representation of the user name if available or null if no user name could be
     *     found.
     */
    public static String getName(org.apache.shiro.subject.Subject subject) {
        return getName(subject, null, false);
    }

    /**
     * Retrieves the user name from a given subject.
     *
     * @param subject Subject to get the user name from.
     * @return String representation of the user name if available or null if no user name could be
     *     found.
     */
    public static String getName(Subject subject, String defaultName) {
        return getName(subject, defaultName, false);
    }

    /**
     * Retrieves the user name from a given subject.
     *
     * @param subject Subject to get the user name from.
     * @param defaultName Name to send back if no user name was found.
     * @param returnDisplayName return formatted user name for displaying
     * @return String representation of the user name if available or defaultName if no user name
     *     could be found or incoming subject was null.
     */
    public static String getName(Subject subject, String defaultName, boolean returnDisplayName) {
        String name = defaultName;
        if (subject != null) {
            PrincipalCollection principals = subject.getPrincipals();
            if (principals != null) {
                SecurityAssertion assertion = principals.oneByType(SecurityAssertion.class);
                if (assertion != null) {
                    Principal principal = assertion.getPrincipal();
                    if (principal instanceof KerberosPrincipal) {
                        StringTokenizer st = new StringTokenizer(principal.getName(), "@");
                        st = new StringTokenizer(st.nextToken(), "/");
                        name = st.nextToken();
                    } else {
                        name = principal.getName();
                    }

                    if (returnDisplayName) {
                        name = getDisplayName(principal, name);
                    }

                } else {
                    // send back the primary principal as a string
                    name = principals.getPrimaryPrincipal().toString();
                }
            } else {
                LOGGER.debug(
                        "No principals located in the incoming subject, cannot look up user name. Using default name of {}.",
                        defaultName);
            }
        } else {
            LOGGER.debug("Incoming subject was null, cannot look up user name. Using default name of {}.",
                    defaultName);
        }

        LOGGER.debug("Sending back name {}.", name);
        return name;
    }

    private static String getExtendedCertAttribute(X500Principal principal, ASN1ObjectIdentifier identifier) {
        RDN[] rdNs = new X500Name(principal.getName()).getRDNs(identifier);
        if (rdNs != null && rdNs.length > 0) {
            AttributeTypeAndValue attributeTypeAndValue = rdNs[0].getFirst();
            if (attributeTypeAndValue != null) {
                return attributeTypeAndValue.getValue().toString();
            }
        }
        return null;
    }

    public static String getCommonName(X500Principal principal) {
        return getExtendedCertAttribute(principal, BCStyle.CN);
    }

    public static String getEmailAddress(X500Principal principal) {
        return getExtendedCertAttribute(principal, BCStyle.EmailAddress);
    }

    public static String getCountry(X500Principal principal) {
        return getExtendedCertAttribute(principal, BCStyle.C);
    }

    public static String filterDN(X500Principal principal, Predicate<RDN> predicate) {
        RDN[] rdns = Arrays.stream(new X500Name(principal.getName()).getRDNs()).filter(predicate)
                .toArray(RDN[]::new);

        return new X500Name(rdns).toString();
    }

    /**
     * Get a subject's email.
     *
     * @param subject
     * @return email or null if not found.
     */
    @Nullable
    public static String getEmailAddress(Subject subject) {
        List<String> values = getAttribute(subject, EMAIL_ADDRESS_CLAIM_URI);

        if (values.isEmpty()) {
            return null;
        }

        return values.get(0);
    }

    /**
     * Get any attribute from a subject by key.
     *
     * @param subject
     * @param key
     * @return attribute values or an empty list if not found.
     */
    public static List<String> getAttribute(@Nullable Subject subject, String key) {
        Validate.notNull(key);

        if (subject == null) {
            LOGGER.debug("Incoming subject was null, cannot look up {}.", key);
            return Collections.emptyList();
        }

        PrincipalCollection principals = subject.getPrincipals();
        if (principals == null) {
            LOGGER.debug("No principals located in the incoming subject, cannot look up {}.", key);
            return Collections.emptyList();
        }

        SecurityAssertion assertion = principals.oneByType(SecurityAssertion.class);
        if (assertion == null) {
            LOGGER.debug("Could not find Security Assertion, cannot look up {}.", key);
            return Collections.emptyList();
        }

        return assertion.getAttributeStatements().stream().flatMap(as -> as.getAttributes().stream())
                .filter(a -> a.getName().equals(key)).flatMap(a -> a.getAttributeValues().stream())
                .filter(o -> o instanceof XSString).map(o -> (XSString) o).map(XSString::getValue)
                .collect(Collectors.toList());
    }

    /**
     * Retrieves the security attributes for the given subject.
     *
     * @param subject the Subject to check
     * @return Map of attributes with the collected values for each
     */
    public static Map<String, SortedSet<String>> getSubjectAttributes(Subject subject) {
        if (subject == null) {
            return Collections.emptyMap();
        }

        return subject.getPrincipals().byType(SecurityAssertion.class).stream()
                .map(SecurityAssertion::getAttributeStatements).flatMap(Collection::stream)
                .map(AttributeStatement::getAttributes).flatMap(Collection::stream)
                .collect(Collectors.toMap(Attribute::getName, SubjectUtils::getAttributeValues, (acc, val) -> {
                    acc.addAll(val);
                    return acc;
                }));
    }

    /**
     * Retrieves the type of the Security Assertion inside the given Subject.
     *
     * @param subject Subject to get the user name from.
     * @return String representation of the user name if available or defaultName if no user name
     *     could be found or incoming subject was null.
     */
    public static String getType(Subject subject) {
        if (subject == null) {
            LOGGER.debug("Incoming subject was null, cannot look up security assertion type.");
            return null;
        }

        PrincipalCollection principals = subject.getPrincipals();
        if (principals == null) {
            LOGGER.debug("No principals located in the incoming subject, cannot look up security assertion type.");
            return null;
        }

        SecurityAssertion assertion = principals.oneByType(SecurityAssertion.class);
        if (assertion == null) {
            LOGGER.debug("No principals located in the incoming subject, cannot look up security assertion type.");
            return null;
        }

        return assertion.getTokenType();
    }

    private static SortedSet<String> getAttributeValues(org.opensaml.saml.saml2.core.Attribute attribute) {
        return attribute.getAttributeValues().stream().filter(XSString.class::isInstance).map(XSString.class::cast)
                .map(XSString::getValue).collect(Collectors.toCollection(TreeSet::new));
    }
}