net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributesMapper.java Source code

Java tutorial

Introduction

Here is the source code for net.shibboleth.idp.saml.attribute.mapping.AbstractSAMLAttributesMapper.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 net.shibboleth.idp.saml.attribute.mapping;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import net.shibboleth.idp.attribute.AttributeEncoder;
import net.shibboleth.idp.attribute.IdPAttribute;
import net.shibboleth.idp.attribute.resolver.AttributeDefinition;
import net.shibboleth.idp.attribute.resolver.AttributeResolver;
import net.shibboleth.idp.saml.attribute.encoding.AttributeMapperProcessor;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;

import org.opensaml.saml.saml2.core.Attribute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Supplier;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
 * The class contains the mechanics to go from a list of {@link Attribute}s (or derived) to a {@link Multimap} of
 * {@link String},{@link IdPAttribute} (or derived, or null). The representation as a {@link Multimap} is useful for
 * filtering situations and is exploited by AttributeInMetadata filter.
 * 
 * @param <InType> the type which is to be inspected and mapped
 * @param <OutType> some sort of representation of an IdP attribute
 */

public abstract class AbstractSAMLAttributesMapper<InType extends Attribute, OutType extends IdPAttribute>
        extends AbstractIdentifiableInitializableComponent implements AttributesMapper<InType, OutType> {

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

    /** The mappers we can apply. */
    @Nonnull
    @NonnullElements
    private Collection<AttributeMapper<InType, OutType>> mappers = Collections.EMPTY_LIST;

    /** The String used to prefix log message. */
    @Nullable
    private String logPrefix;

    /** Default Constructor. */
    public AbstractSAMLAttributesMapper() {

    }

    /**
     * Constructor to create the mapping from an existing resolver. <br/>
     * This code inverts the {@link AttributeEncoder} (internal attribute -> SAML Attributes) into
     * {@link AttributeMapper} (SAML [RequestedAttributes] -> internal [Requested] Attributes). <br/>
     * to generate the {@link AbstractSAMLAttributeMapper} (with no
     * {@link AbstractSAMLAttributeMapper#getAttributeIds()}. These are accumulated into a {@link Multimap}, where the
     * key is the {@link AbstractSAMLAttributeMapper} and the values are the (IdP) attribute names. The collection of
     * {@link AttributeMapper}s can then be extracted from the map, and the appropriate internal names added (these
     * being the value of the {@link Multimap})
     * 
     * @param resolver The resolver
     * @param id The it
     * @param mapperFactory A factory to generate new mappers of the correct type.
     */
    public AbstractSAMLAttributesMapper(@Nonnull final AttributeResolver resolver,
            @Nonnull @NotEmpty final String id,
            @Nonnull final Supplier<AbstractSAMLAttributeMapper<InType, OutType>> mapperFactory) {

        setId(id);

        final Multimap<AbstractSAMLAttributeMapper<InType, OutType>, String> theMappers;

        theMappers = HashMultimap.create();

        for (final AttributeDefinition attributeDef : resolver.getAttributeDefinitions().values()) {
            for (final AttributeEncoder encode : attributeDef.getAttributeEncoders()) {
                if (encode instanceof AttributeMapperProcessor) {
                    // There is an appropriate reverse mapper
                    final AttributeMapperProcessor factory = (AttributeMapperProcessor) encode;
                    final AbstractSAMLAttributeMapper<InType, OutType> mapper = mapperFactory.get();
                    factory.populateAttributeMapper(mapper);

                    theMappers.put(mapper, attributeDef.getId());
                }
            }
        }

        mappers = new ArrayList<>(theMappers.values().size());

        for (final Entry<AbstractSAMLAttributeMapper<InType, OutType>, Collection<String>> entry : theMappers
                .asMap().entrySet()) {

            final AbstractSAMLAttributeMapper<InType, OutType> mapper = entry.getKey();
            mapper.setAttributeIds(new ArrayList<>(entry.getValue()));
            mappers.add(mapper);
        }
    }

    /**
     * Get the mappers.
     * 
     * @return Returns the mappers.
     */
    @Nonnull
    @NonnullElements
    public Collection<AttributeMapper<InType, OutType>> getMappers() {
        return mappers;
    }

    /**
     * Set the attribute mappers into the lookup map.
     * 
     * @param theMappers The mappers to set.
     */
    public void setMappers(@Nonnull Collection<AttributeMapper<InType, OutType>> theMappers) {
        mappers = Constraint.isNotNull(theMappers, "mappers list must be non null");
    }

    /** {@inheritDoc} */
    @Override
    @Nonnull
    @NonnullElements
    public Multimap<String, OutType> mapAttributes(@Nonnull @NonnullElements final List<InType> prototypes) {

        final Multimap<String, OutType> result = ArrayListMultimap.create();

        for (final InType prototype : prototypes) {
            for (final AttributeMapper<InType, OutType> mapper : mappers) {

                final Map<String, OutType> mappedAttributes;
                try {
                    mappedAttributes = mapper.mapAttribute(prototype);
                } catch (Exception e) {
                    log.error("{} Attempt to map SAML attribute '{}' with mapper '{}' failed: ", getLogPrefix(),
                            prototype.getName(), mapper.getId(), e);
                    throw e;
                }

                log.debug("{} SAML attribute '{}' mapped to {} attributes by mapper '{}'", getLogPrefix(),
                        prototype.getName(), mappedAttributes.size(), mapper.getId());

                for (final Entry<String, OutType> entry : mappedAttributes.entrySet()) {
                    result.put(entry.getKey(), entry.getValue());
                }
            }
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    protected void doInitialize() throws ComponentInitializationException {
        super.doInitialize();
        logPrefix = null;
        for (final AttributeMapper mapper : mappers) {
            ComponentSupport.initialize(mapper);
        }
    }

    /**
     * Return a string which is to be prepended to all log messages.
     * 
     * @return "Attribute Mappers '<ID>' :"
     */
    @Nonnull
    @NotEmpty
    private String getLogPrefix() {
        String s = logPrefix;
        if (null == s) {
            s = new StringBuilder("Attribute Mappers : '").append(getId()).append("':").toString();
            logPrefix = s;
        }
        return s;
    }
}