ddf.security.sts.SecurityAttributesClaimsHandler.java Source code

Java tutorial

Introduction

Here is the source code for ddf.security.sts.SecurityAttributesClaimsHandler.java

Source

/**
 * Copyright (c) Codice Foundation
 * 
 * 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.
 * 
 * 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.sts;

import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.sts.claims.Claim;
import org.apache.cxf.sts.claims.ClaimCollection;
import org.apache.cxf.sts.claims.ClaimsHandler;
import org.apache.cxf.sts.claims.ClaimsParameters;
import org.apache.cxf.sts.claims.RequestClaimCollection;
import org.apache.ws.security.WSConstants;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.x500.X500Principal;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;

/**
 * The {@link SecurityAttributesClaimsHandler} is called by the ClaimsManager to handle the
 * securityAttributeClaimType. This class will retrieve the security attributes from LDAP for a
 * specified user. The Claim Type is configurable.
 * 
 */
public class SecurityAttributesClaimsHandler implements ClaimsHandler {

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

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

    private static final String ATTRIBUTE_DELIMITER = ", ";

    private static final String EQUALS_DELIMITER = "=";

    private String securityAttributeClaimType = DEFAULT_SECURITY_CLAIM_TYPE;

    private String attributeMapping;

    private String userBaseDn;

    private Map<String, String> claimsLdapAttributeMapping;

    private String objectClassName;

    private String uidAttribute;

    private LdapTemplate ldapTemplate;

    public LdapTemplate getLdapTemplate() {
        return ldapTemplate;
    }

    public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }

    public String getObjectClassName() {
        return objectClassName;
    }

    public void setObjectClassName(String objectClassName) {
        this.objectClassName = objectClassName;
    }

    public String getUidAttribute() {
        return uidAttribute;
    }

    public void setUidAttribute(String uidAttribute) {
        this.uidAttribute = uidAttribute;
    }

    public Map<String, String> getClaimsLdapAttributeMapping() {
        return claimsLdapAttributeMapping;
    }

    public String getUserBaseDn() {
        return userBaseDn;
    }

    public void setUserBaseDn(String userBaseDn) {
        this.userBaseDn = userBaseDn;
    }

    public String getAttributeMapping() {
        return attributeMapping;
    }

    public void setAttributeMapping(String attributesToMap) {
        if (attributesToMap != null && !attributesToMap.equals(this.attributeMapping)) {
            setClaimsLdapAttributeMapping(buildLdapClaimsMap(attributesToMap));
        }
        this.attributeMapping = attributesToMap;
    }

    private Map<String, String> buildLdapClaimsMap(String attributesToMap) {
        // Remove first and last character since they are "[" and "]"
        String cleanedAttributesToMap = attributesToMap.substring(1, attributesToMap.length() - 1);
        String[] attributes = cleanedAttributesToMap.split(ATTRIBUTE_DELIMITER);
        Map<String, String> map = new HashMap<String, String>();
        for (String attribute : attributes) {
            String[] attrSplit = attribute.split(EQUALS_DELIMITER);
            map.put(attrSplit[0], attrSplit[1]);
        }
        return map;
    }

    private void setClaimsLdapAttributeMapping(Map<String, String> mapping) {
        this.claimsLdapAttributeMapping = mapping;

    }

    public String getSecurityAttributeClaimType() {
        return securityAttributeClaimType;
    }

    public void setSecurityAttributeClaimType(String securityAttributeClaimType) {
        this.securityAttributeClaimType = securityAttributeClaimType;
    }

    @Override
    public List<URI> getSupportedClaimTypes() {
        List<URI> uriList = new ArrayList<URI>();
        uriList.add(getSecurityAttributeURI());

        return uriList;
    }

    private URI getSecurityAttributeURI() {
        URI uri = null;
        try {
            uri = new URI(securityAttributeClaimType);
        } catch (URISyntaxException e) {
            LOGGER.warn("Unable to add securityAttributes claim type.", e);
        }
        return uri;
    }

    // Builds the Security Attributes into an array.
    private String[] buildAttributes() {
        // Get the attribute values from the map.
        String[] attributes = new String[claimsLdapAttributeMapping.size()];
        int index = 0;
        for (Entry<String, String> entry : claimsLdapAttributeMapping.entrySet()) {
            attributes[index] = entry.getValue();
            index++;
        }
        return attributes;
    }

    private static class DdfAttributesMapper implements AttributesMapper {

        public Object mapFromAttributes(Attributes attrs) throws NamingException {
            Map<String, Attribute> map = new HashMap<String, Attribute>();
            NamingEnumeration<? extends Attribute> attrEnum = attrs.getAll();
            while (attrEnum.hasMore()) {
                Attribute att = attrEnum.next();
                map.put(att.getID(), att);
            }
            return map;
        }
    };

    @Override
    public ClaimCollection retrieveClaimValues(RequestClaimCollection claims, ClaimsParameters parameters) {
        String[] attributes = buildAttributes();
        ClaimCollection claimsColl = new ClaimCollection();
        try {

            String user = getUserFromClaimsParameters(parameters);

            if (user == null) {
                LOGGER.warn("User must not be null");
                return claimsColl;
            } else {
                LOGGER.trace("Retrieve securityAttributes claims for user {}", user);
            }

            AndFilter filter = buildLdapFilter(user);

            AttributesMapper mapper = new DdfAttributesMapper();

            List<?> results = ldapTemplate.search(userBaseDn, filter.toString(), SearchControls.SUBTREE_SCOPE,
                    attributes, mapper);

            for (Object result : results) {
                Map<String, Attribute> ldapAttributes = null;
                ldapAttributes = CastUtils.cast((Map<?, ?>) result);

                // Get each of the mapped Attributes from the result.
                for (Entry<String, String> claimAttr : claimsLdapAttributeMapping.entrySet()) {
                    Attribute attr = ldapAttributes.get(claimAttr.getValue());
                    if (attr == null) {
                        LOGGER.trace("Claim '{}' is null", claimAttr.getKey());
                    } else {
                        Claim c = buildClaim(parameters, claimAttr, attr);
                        claimsColl.add(c);
                    }
                }
            }
        } catch (URISyntaxException e) {
            LOGGER.error("Unable to set role claims.", e);
        }
        return claimsColl;
    }

    /*
     * Helper method to build a Claim.
     */
    private Claim buildClaim(ClaimsParameters parameters, Entry<String, String> claimAttr, Attribute attr)
            throws URISyntaxException {
        Claim c = new Claim();
        c.setClaimType(new URI(claimAttr.getKey()));
        c.setPrincipal(parameters.getPrincipal());

        StringBuilder claimValue = new StringBuilder();
        try {
            NamingEnumeration<?> list = (NamingEnumeration<?>) attr.getAll();
            while (list.hasMore()) {
                Object obj = list.next();
                if (!(obj instanceof String)) {
                    LOGGER.warn("LDAP attribute '{}' has an unsupported value type", claimAttr.getValue());
                    break;
                }
                claimValue.append((String) obj);
                if (list.hasMore()) {
                    claimValue.append(ATTRIBUTE_DELIMITER);
                }
            }
        } catch (NamingException ex) {
            LOGGER.warn("Failed to read value of LDAP attribute '{}'", claimAttr.getValue());
        }

        c.setValue(claimValue.toString());
        return c;
    }

    /**
     * @param parameters
     * @param claimsColl
     * @param principal
     * @return
     */
    private String getUserFromClaimsParameters(ClaimsParameters parameters) {
        Principal principal = parameters.getPrincipal();
        String user = null;
        if (parameters.getAdditionalProperties() != null
                && parameters.getAdditionalProperties().containsKey(WSConstants.USERNAME_LN)) {
            user = (String) parameters.getAdditionalProperties().get(WSConstants.USERNAME_LN);
        } else {
            if (principal instanceof KerberosPrincipal) {
                KerberosPrincipal kp = (KerberosPrincipal) principal;
                StringTokenizer st = new StringTokenizer(kp.getName(), "@");
                user = st.nextToken();
            } else if (principal instanceof X500Principal) {
                // 1.2.840.113549.1.9.1=#160d69346365406c6d636f2e636f6d,CN=client,OU=I4CE,O=Lockheed
                // Martin,L=Goodyear,ST=Arizona,C=US
                X500Principal xp = (X500Principal) principal;
                StringTokenizer st = new StringTokenizer(xp.getName(), ",");
                @SuppressWarnings("unused")
                String syntaxAndUniqueId = st.nextToken();
                String cn = st.nextToken();
                // String ou = st.nextToken();
                // String o = st.nextToken();
                // String loc = st.nextToken();
                // String state = st.nextToken();
                // String country = st.nextToken();

                StringTokenizer userTokenizer = new StringTokenizer(cn, "=");
                // String cnKey = userTokenizer.nextToken();
                user = userTokenizer.nextToken();
            } else if (principal != null) {
                user = principal.getName();
            } else {
                LOGGER.warn("Principal is null");
            }
        }

        return user;
    }

    /*
     * Method to determine the filter clause for LDAP. If an objectClassName is provided the query
     * will be built as "(&(objectclass=<ocName>)(uid=<username>))". If the objectClassName is not
     * provided the filter will be built as "(&(uid=<username>))". The uidAttribute is also
     * configurable for different LDAP implementations.
     */
    private AndFilter buildLdapFilter(String user) {
        AndFilter filter = new AndFilter();
        if (getObjectClassName() == null || getObjectClassName().isEmpty()) {
            filter.and(new EqualsFilter(getUidAttribute(), user));
        } else {
            filter.and(new EqualsFilter("objectClass", getObjectClassName()))
                    .and(new EqualsFilter(getUidAttribute(), user));
        }
        return filter;
    }
}