edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAML2AttributeAuthority.java Source code

Java tutorial

Introduction

Here is the source code for edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAML2AttributeAuthority.java

Source

/*
 * Licensed to the University Corporation for Advanced Internet Development, 
 * Inc. (UCAID) under one or more contributor license agreements.  See the 
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID licenses this file to You 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 edu.internet2.middleware.shibboleth.common.attribute.provider;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.opensaml.Configuration;
import org.opensaml.common.SAMLObjectBuilder;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeQuery;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.RequestAbstractType;
import org.opensaml.saml2.core.StatusResponseType;
import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncoder;
import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncodingException;
import edu.internet2.middleware.shibboleth.common.attribute.encoding.SAML2AttributeEncoder;
import edu.internet2.middleware.shibboleth.common.attribute.filtering.provider.ShibbolethAttributeFilteringEngine;
import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethAttributeResolver;
import edu.internet2.middleware.shibboleth.common.config.BaseService;
import edu.internet2.middleware.shibboleth.common.profile.provider.SAMLProfileRequestContext;
import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.AbstractSAML2ProfileConfiguration;
import edu.internet2.middleware.shibboleth.common.service.ServiceException;

/**
 * SAML 2.0 Attribute Authority.
 */
public class ShibbolethSAML2AttributeAuthority extends BaseService implements SAML2AttributeAuthority {

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(ShibbolethSAML2AttributeAuthority.class);

    /** For building attribute statements. */
    private SAMLObjectBuilder<AttributeStatement> statementBuilder;

    /** Attribute resolver. */
    private ShibbolethAttributeResolver attributeResolver;

    /** To determine releasable attributes. */
    private ShibbolethAttributeFilteringEngine filteringEngine;

    /**
     * This creates a new attribute authority.
     * 
     * @param resolver The attribute resolver to set
     */
    @SuppressWarnings("unchecked")
    public ShibbolethSAML2AttributeAuthority(ShibbolethAttributeResolver resolver) {

        XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
        statementBuilder = (SAMLObjectBuilder<AttributeStatement>) builderFactory
                .getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME);

        attributeResolver = resolver;
    }

    /**
     * Gets the attribute resolver.
     * 
     * @return Returns the attributeResolver.
     */
    public ShibbolethAttributeResolver getAttributeResolver() {
        return attributeResolver;
    }

    /**
     * Gets the filtering engine.
     * 
     * @return Returns the filteringEngine.
     */
    public ShibbolethAttributeFilteringEngine getFilteringEngine() {
        return filteringEngine;
    }

    /**
     * Sets the attribute filtering engine.
     * 
     * @param engine attribute filtering engine
     */
    public void setFilteringEngine(ShibbolethAttributeFilteringEngine engine) {
        filteringEngine = engine;
    }

    /** {@inheritDoc} */
    public AttributeStatement buildAttributeStatement(AttributeQuery query, Collection<BaseAttribute> attributes)
            throws AttributeEncodingException {

        Collection<Attribute> encodedAttributes = encodeAttributes(attributes);

        filterAttributesByValue(query, encodedAttributes);

        if (!encodedAttributes.isEmpty()) {
            AttributeStatement statement = statementBuilder.buildObject();
            List<org.opensaml.saml2.core.Attribute> samlAttributes = statement.getAttributes();
            samlAttributes.addAll(encodedAttributes);
            return statement;
        } else {
            log.debug("No attributes remained after encoding and filtering by value, no attribute statement built");
            return null;
        }
    }

    /** {@inheritDoc} */
    public String getAttributeIDBySAMLAttribute(Attribute attribute) {
        // TODO Auto-generated method stub
        return null;
    }

    /** {@inheritDoc} */
    public Attribute getSAMLAttributeByAttributeID(String id) {
        // TODO Auto-generated method stub
        return null;
    }

    /** {@inheritDoc} */
    public String getPrincipal(
            SAMLProfileRequestContext<? extends RequestAbstractType, ? extends StatusResponseType, NameID, ? extends AbstractSAML2ProfileConfiguration> requestContext)
            throws AttributeRequestException {
        if (requestContext.getInboundMessageIssuer() == null || requestContext.getSubjectNameIdentifier() == null) {
            throw new AttributeRequestException(
                    "Unable to resolve principal, attribute request ID and subject name identifier may not be null");
        }

        return attributeResolver.resolvePrincipalName(requestContext);
    }

    /** {@inheritDoc} */
    public Map<String, BaseAttribute> getAttributes(
            SAMLProfileRequestContext<? extends RequestAbstractType, ? extends StatusResponseType, NameID, ? extends AbstractSAML2ProfileConfiguration> requestContext)
            throws AttributeRequestException {
        HashSet<String> requestedAttributes = new HashSet<String>();

        // get attributes from the message
        Set<String> queryAttributeIds = getAttributeIds(requestContext.getInboundSAMLMessage());
        requestedAttributes.addAll(queryAttributeIds);

        // get attributes from metadata
        Set<String> metadataAttributeIds = getAttribtueIds(requestContext.getPeerEntityMetadata());
        requestedAttributes.addAll(metadataAttributeIds);

        requestContext.setRequestedAttributes(requestedAttributes);

        // Resolve attributes
        Map<String, BaseAttribute> attributes = attributeResolver.resolveAttributes(requestContext);

        // Filter resulting attributes
        if (filteringEngine != null) {
            attributes = filteringEngine.filterAttributes(attributes, requestContext);
        }

        return attributes;
    }

    /**
     * This encodes the supplied attributes with that attribute's SAML2 encoder.
     * 
     * @param attributes the attributes to encode
     * 
     * @return the encoded attributes
     * 
     * @throws AttributeEncodingException thrown if an attribute could not be encoded
     */
    @SuppressWarnings("unchecked")
    protected Collection<Attribute> encodeAttributes(Collection<BaseAttribute> attributes)
            throws AttributeEncodingException {
        Collection<Attribute> encodedAttributes = new ArrayList<Attribute>();

        boolean attributeEncoded;

        for (BaseAttribute<?> shibbolethAttribute : attributes) {
            attributeEncoded = false;

            if (shibbolethAttribute.getValues() == null || shibbolethAttribute.getValues().size() == 0) {
                continue;
            }

            Attribute attribute;
            for (AttributeEncoder encoder : shibbolethAttribute.getEncoders()) {
                if (encoder instanceof SAML2AttributeEncoder) {
                    try {
                        attribute = (Attribute) encoder.encode(shibbolethAttribute);
                        if (attribute != null) {
                            encodedAttributes.add(attribute);
                            attributeEncoded = true;
                            log.debug("Encoded attribute {} with encoder of type {}", shibbolethAttribute.getId(),
                                    encoder.getClass().getName());
                        }
                    } catch (AttributeEncodingException e) {
                        log.warn("unable to encode attribute: " + shibbolethAttribute.getId(), e);
                    }
                }
            }

            // if it couldn't be encoded log it
            if (!attributeEncoded) {
                log.debug("Attribute {} was not encoded because no SAML2AttributeEncoder was attached to it.",
                        shibbolethAttribute.getId());
            }
        }

        return encodedAttributes;
    }

    /**
     * Filters out all but the values, for an attribute, provided in the query, if and only if, the query specifies at
     * least one value for the attribute. That is to say, if the attribute query does not specify any attribute values
     * then all values for that attribute are accepted and remain. Because this comparison acts on the marshalled form
     * the provided attributes will be encoded prior to filtering.
     * 
     * @param query the attribute query
     * @param attributes the attributes to filter
     */
    protected void filterAttributesByValue(AttributeQuery query, Collection<Attribute> attributes) {
        if (query == null) {
            return;
        }

        // TODO not implemented yet
    }

    /**
     * Gets the attribute IDs for those attributes requested in the attribute query.
     * 
     * @param samlRequest incomming SAML request
     * 
     * @return attribute IDs for those attributes requested in the attribute query
     */
    protected Set<String> getAttributeIds(RequestAbstractType samlRequest) {
        Set<String> queryAttributeIds = new HashSet<String>();
        if (!(samlRequest instanceof AttributeQuery)) {
            return queryAttributeIds;
        }

        AttributeQuery query = (AttributeQuery) samlRequest;
        if (query != null) {
            List<org.opensaml.saml2.core.Attribute> queryAttributes = query.getAttributes();
            queryAttributeIds = getAttributeIds(queryAttributes);
            log.debug("query message contains the following attributes: {}", queryAttributeIds);
        }

        return queryAttributeIds;
    }

    /**
     * Gets the attribute IDs for those attributes requested in the entity metadata.
     * 
     * @param metadata the entity metadata
     * 
     * @return attribute IDs for those attributes requested in the entity metadata
     */
    protected Set<String> getAttribtueIds(EntityDescriptor metadata) {
        Set<String> metadataAttributeIds = new HashSet<String>();
        AttributeAuthorityDescriptor aaDescriptor;
        if (metadata != null) {
            aaDescriptor = metadata.getAttributeAuthorityDescriptor(SAMLConstants.SAML20P_NS);
            if (aaDescriptor != null) {
                List<org.opensaml.saml2.core.Attribute> metadataAttributes = aaDescriptor.getAttributes();
                metadataAttributeIds = getAttributeIds(metadataAttributes);
                log.debug("metadata contains the following attributes: {}", metadataAttributeIds);
            }
        }

        return metadataAttributeIds;
    }

    /**
     * This parses the attribute ids from the supplied list of attributes.
     * 
     * @param attributes <code>List</code>
     * @return <code>Set</code> of attribute ids
     */
    protected Set<String> getAttributeIds(List<org.opensaml.saml2.core.Attribute> attributes) {
        final Set<String> attributeIds = new HashSet<String>();
        for (org.opensaml.saml2.core.Attribute a : attributes) {
            String attrId = getAttributeIDBySAMLAttribute(a);
            if (attrId != null) {
                attributeIds.add(attrId);
            }
        }
        return attributeIds;
    }

    /** {@inheritDoc} */
    protected void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException {

    }
}