[java-identity-provider] branch master updated: IDP-1494 - Login flow for proxied authentication

Scott Cantor cantor.2 at osu.edu
Wed Oct 30 13:27:08 EDT 2019


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=b8a0d1b11fa31ede9d108eba70309698fcb2365d

The following commit(s) were added to refs/heads/master by this push:
       new  b8a0d1b   IDP-1494 - Login flow for proxied authentication
b8a0d1b is described below

commit b8a0d1b11fa31ede9d108eba70309698fcb2365d
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Oct 30 13:25:16 2019 -0400

    IDP-1494 - Login flow for proxied authentication
    
    https://issues.shibboleth.net/jira/browse/IDP-1494
    
    Start to flesh out result end of SAML flow.
---
 .../system/flows/authn/proxy/saml-proxy-beans.xml  |  12 +-
 .../system/flows/authn/proxy/saml-proxy-flow.xml   |   1 +
 idp-saml-impl/pom.xml                              |   5 +
 .../saml/saml2/profile/impl/AddAuthnRequest.java   |   2 +-
 ...cation.java => ContinueSAMLAuthentication.java} |  18 +-
 .../saml/saml2/profile/impl/SAMLAuthnContext.java  |  55 +++-
 .../profile/impl/ValidateSAMLAuthentication.java   | 338 +++++++++++++++++++--
 7 files changed, 397 insertions(+), 34 deletions(-)

diff --git a/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml b/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml
index 547120c..6c81b89 100644
--- a/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-beans.xml
@@ -213,9 +213,19 @@
         p:httpServletRequest-ref="shibboleth.HttpServletRequest"
         p:bindingDescriptor-ref="shibboleth.Binding.SAML2POST" />
 
+    <bean id="ContinueSAMLAuthentication"
+        class="net.shibboleth.idp.saml.saml2.profile.impl.ContinueSAMLAuthentication" scope="prototype"
+        p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+
     <bean id="ValidateSAMLAuthentication"
         class="net.shibboleth.idp.saml.saml2.profile.impl.ValidateSAMLAuthentication" scope="prototype"
         p:authenticationContextLookupStrategy-ref="ParentAuthenticiationContextLookup"
-        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext" />
+        p:profileContextLookupStrategy-ref="shibboleth.ChildLookup.ProxyProfileRequestContext"
+        p:responderLookupStrategy-ref="shibboleth.RelyingPartyIdLookup.Simple"
+        p:requesterLookupStrategy-ref="shibboleth.ResponderIdLookup.Simple"
+        p:transcoderRegistry-ref="shibboleth.AttributeRegistryService"
+        p:attributeFilter-ref="shibboleth.AttributeFilterService"
+        p:metadataResolver-ref="shibboleth.MetadataResolver" />
 
 </beans>
diff --git a/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-flow.xml b/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-flow.xml
index 9991204..4803cba 100644
--- a/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/proxy/saml-proxy-flow.xml
@@ -45,6 +45,7 @@
     </view-state>
     
     <action-state id="ProcessResponse">
+        <evaluate expression="ContinueSAMLAuthentication" />
         <evaluate expression="ValidateSAMLAuthentication" />
         <evaluate expression="'proceed'" />
         
diff --git a/idp-saml-impl/pom.xml b/idp-saml-impl/pom.xml
index 673dba1..45298bb 100644
--- a/idp-saml-impl/pom.xml
+++ b/idp-saml-impl/pom.xml
@@ -56,6 +56,11 @@
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>idp-attribute-filter-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>idp-attribute-resolver-api</artifactId>
             <version>${project.version}</version>
         </dependency>
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
index b50a733..7201449 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
@@ -80,7 +80,7 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
     /** Strategy used to obtain the relay state token to provide. */
     @Nonnull private Function<ProfileRequestContext,String> relayStateLookupStrategy;
     
-    /** Strategy used to obtain the response issuer value. */
+    /** Strategy used to obtain the request issuer value. */
     @Nullable private Function<ProfileRequestContext,String> issuerLookupStrategy;
     
     /** The generator to use. */
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ContinueSAMLAuthentication.java
similarity index 79%
copy from idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
copy to idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ContinueSAMLAuthentication.java
index a89ca72..08e21c7 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ContinueSAMLAuthentication.java
@@ -24,11 +24,13 @@ import net.shibboleth.idp.authn.AbstractAuthenticationAction;
 import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
+import net.shibboleth.utilities.java.support.logic.Constraint;
 
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.StatusCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,10 +48,10 @@ import org.slf4j.LoggerFactory;
  * @event {@link AuthnEventIds#NO_CREDENTIALS}
  * @event various
  */
-public class ValidateSAMLAuthentication extends AbstractAuthenticationAction {
+public class ContinueSAMLAuthentication extends AbstractAuthenticationAction {
 
     /** Class logger. */
-    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateSAMLAuthentication.class);
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ContinueSAMLAuthentication.class);
 
     /** Context containing the result to examine. */
     @Nullable private ExternalAuthenticationContext extContext;
@@ -89,6 +91,18 @@ public class ValidateSAMLAuthentication extends AbstractAuthenticationAction {
             log.info("{} Inbound message was not a SAML Response", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, EventIds.MESSAGE_PROC_ERROR);
         }
+        
+        // TODO: this is dummy code to be removed once we have legitimate processing in place.
+        final Response response = (Response) profileRequestContext.getInboundMessageContext().getMessage();
+        if (StatusCode.SUCCESS.equals(response.getStatus().getStatusCode().getValue())) {
+            Constraint.isTrue(response.getAssertions().size() == 1, "Wrong assertion count");
+            Constraint.isTrue(response.getAssertions().get(0).getAuthnStatements().size() == 1, "Wrong statement count");
+            authenticationContext.getSubcontext(SAMLAuthnContext.class)
+                .setSubject(response.getAssertions().get(0).getSubject())
+                .setAuthnStatement(response.getAssertions().get(0).getAuthnStatements().get(0));
+        } else {
+            authenticationContext.removeSubcontext(SAMLAuthnContext.class);
+        }
     }
     
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SAMLAuthnContext.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SAMLAuthnContext.java
index 9ee5dc1..4083060 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SAMLAuthnContext.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/SAMLAuthnContext.java
@@ -26,12 +26,13 @@ import org.opensaml.messaging.context.BaseContext;
 import org.opensaml.messaging.decoder.MessageDecoder;
 import org.opensaml.messaging.handler.MessageHandler;
 import org.opensaml.profile.action.ProfileAction;
+import org.opensaml.saml.saml2.core.AuthnStatement;
+import org.opensaml.saml.saml2.core.Subject;
 
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 /**
- * Manages state for the webflow -> external -> webflow transitions
- * during proxied SAML authentication.
+ * Manages state during proxied SAML authentication.
  * 
  * @since 4.0.0
  */
@@ -46,6 +47,12 @@ public class SAMLAuthnContext extends BaseContext {
     /** The function to use to obtain a decoder. */
     @Nonnull private Function<String,MessageDecoder> decoderFactory;
     
+    /** Subject of assertion used to authenticate. */
+    @Nullable private Subject subject;
+    
+    /** Authentication statement. */
+    @Nullable private AuthnStatement authnStatement;
+    
     /**
      * Constructor.
      *
@@ -98,4 +105,48 @@ public class SAMLAuthnContext extends BaseContext {
         return this;
     }
     
+    /**
+     * Get the SAML {@link Subject} from the authentication.
+     * 
+     * @return SAML {@link Subject}
+     */
+    @Nullable public Subject getSubject() {
+        return subject;
+    }
+ 
+    /**
+     * Set the SAML {@link Subject} from the authentication.
+     * 
+     * @param sub the SAML {@link Subject}
+     * 
+     * @return this context
+     */
+    @Nonnull public SAMLAuthnContext setSubject(@Nullable final Subject sub) {
+        subject = sub;
+        
+        return this;
+    }
+    
+    /**
+     * Get the SAML {@link AuthnStatement} from the authentication.
+     * 
+     * @return SAML {@link AuthnStatement}
+     */
+    @Nullable public AuthnStatement getAuthnStatement() {
+        return authnStatement;
+    }
+ 
+    /**
+     * Set the SAML {@link AuthnStatement} from the authentication.
+     * 
+     * @param statement the SAML {@link AuthnStatement}
+     * 
+     * @return this context
+     */
+    @Nonnull public SAMLAuthnContext setAuthnStatement(@Nullable final AuthnStatement statement) {
+        authnStatement = statement;
+        
+        return this;
+    }
+
 }
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
index a89ca72..67e008d 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
@@ -17,42 +17,146 @@
 
 package net.shibboleth.idp.saml.saml2.profile.impl;
 
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.security.auth.Subject;
 
-import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.attribute.AttributeDecodingException;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.attribute.filter.AttributeFilter;
+import net.shibboleth.idp.attribute.filter.AttributeFilterException;
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext.Direction;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoder;
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.attribute.transcoding.TranscoderSupport;
+import net.shibboleth.idp.attribute.transcoding.TranscodingRule;
+import net.shibboleth.idp.authn.AbstractValidationAction;
 import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
-import net.shibboleth.idp.authn.context.ExternalAuthenticationContext;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
+import net.shibboleth.idp.authn.principal.ProxyAuthenticationPrincipal;
+import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.idp.saml.profile.context.navigate.SAMLMetadataContextLookupFunction;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+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.ComponentSupport;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
 
+import org.opensaml.messaging.context.navigate.RecursiveTypedParentContextLookup;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.metadata.resolver.MetadataResolver;
+import org.opensaml.saml.saml2.core.Assertion;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.AuthenticatingAuthority;
 import org.opensaml.saml.saml2.core.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Strings;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
 /**
- * An action that checks for an {@link ExternalAuthenticationContext} for a signaled event via the
- * {@link ExternalAuthenticationContext#getAuthnError()} method, and otherwise enforces the presence
- * of an inbound SAML Response to process.
+ * An action that produces an {@link net.shibboleth.idp.authn.AuthenticationResult} based on an inbound
+ * SAML 2.0 SSO response.
  * 
- * <p>This is a bridge from the external portion of the SAML proxy implementation to transition
- * back into the flow and pick up any signaled errors if necessary.</p>
+ * <p>A {@link SAMLAuthnContext} is used as the basis of the result and the lack of a context is a signal
+ * to record a failure. Actual validation is all upstream of this action, but the use of the ValidationAction
+ * subclass is a convenience for auditing and handling the result.</p>
  *  
  * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@Link EventIds#MESSAGE_PROC_ERROR}
- * @event {@link AuthnEventIds#INVALID_AUTHN_CTX}
- * @event {@link AuthnEventIds#NO_CREDENTIALS}
- * @event various
+ * @event {@link EventIds#INVALID_PROFILE_CTX}
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class).getAttemptedFlow() != null</pre>
+ * @post If AuthenticationContext.getSubcontext(SAMLAuthnContext.class) != null, then
+ * an {@link net.shibboleth.idp.authn.AuthenticationResult} is saved to the {@link AuthenticationContext}.
  */
-public class ValidateSAMLAuthentication extends AbstractAuthenticationAction {
+public class ValidateSAMLAuthentication extends AbstractValidationAction {
+
+    /** Default prefix for metrics. */
+    @Nonnull @NotEmpty private static final String DEFAULT_METRIC_NAME = "net.shibboleth.idp.authn.saml"; 
 
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateSAMLAuthentication.class);
 
-    /** Context containing the result to examine. */
-    @Nullable private ExternalAuthenticationContext extContext;
+    /** Transcoder registry service object. */
+    @Nullable private ReloadableService<AttributeTranscoderRegistry> transcoderRegistry;
+
+    /** Service used to get the engine used to filter attributes. */
+    @Nullable private ReloadableService<AttributeFilter> attributeFilterService;
+
+    /** Strategy used to obtain our identity. */
+    @Nullable private Function<ProfileRequestContext,String> recipientIdLookupStrategy;
+    
+    /** Optional supplemental metadata source for filtering. */
+    @Nullable private MetadataResolver metadataResolver;
+    
+    /** Context containing the result to validate. */
+    @Nullable private SAMLAuthnContext samlAuthnContext;
+    
+    /** Free-standing context for externally supplied inbound attributes. */
+    @Nullable private AttributeContext attributeContext;
+        
+    /** Constructor. */
+    public ValidateSAMLAuthentication() {
+        setMetricName(DEFAULT_METRIC_NAME);
+        recipientIdLookupStrategy = new ResponderIdLookupFunction();
+    }
+
+    /**
+     * Sets the registry of transcoding rules to apply to encode attributes.
+     * 
+     * @param registry registry service interface
+     */
+    public void setTranscoderRegistry(@Nullable final ReloadableService<AttributeTranscoderRegistry> registry) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        transcoderRegistry = registry;
+    }
+    
+
+    /**
+     * Sets the filter service to use for inbound attributes.
+     *
+     * @param filterService optional filter service for inbound attributes
+     */
+    public void setAttributeFilter(@Nullable final ReloadableService<AttributeFilter> filterService) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        attributeFilterService = filterService;
+    }
+    
+    /**
+     * Set a metadata source to use during filtering.
+     * 
+     * @param resolver metadata resolver
+     */
+    public void setMetadataResolver(@Nullable final MetadataResolver resolver) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        metadataResolver = resolver;
+    }
+    
+    /**
+     * Set the strategy used to locate the attribute recipient value to use.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setRecipientIdLookupStrategy(@Nullable final Function<ProfileRequestContext,String> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        recipientIdLookupStrategy = strategy;
+    }
 
     /** {@inheritDoc} */
     @Override
@@ -63,10 +167,18 @@ public class ValidateSAMLAuthentication extends AbstractAuthenticationAction {
             return false;
         }
         
-        extContext = authenticationContext.getSubcontext(ExternalAuthenticationContext.class);
-        if (extContext == null) {
-            log.debug("{} No ExternalAuthenticationContext available within authentication context", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+        if (authenticationContext.getAttemptedFlow() == null) {
+            log.debug("{} No attempted flow within authentication context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
+            recordFailure();
+            return false;
+        }
+        
+        samlAuthnContext = authenticationContext.getSubcontext(SAMLAuthnContext.class);
+        if (samlAuthnContext == null) {
+            log.debug("{} No SAMLAuthnContext available within authentication context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+            recordFailure();
             return false;
         }
         
@@ -78,17 +190,187 @@ public class ValidateSAMLAuthentication extends AbstractAuthenticationAction {
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
             @Nonnull final AuthenticationContext authenticationContext) {
 
-        if (extContext.getAuthnError() != null) {
-            log.info("{} SAML authentication attempt signaled an error: {}", getLogPrefix(),
-                    extContext.getAuthnError());
-            ActionSupport.buildEvent(profileRequestContext, extContext.getAuthnError());
-        } else if (profileRequestContext.getInboundMessageContext() == null) {
-            log.info("{} No inbound SAML Response found", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
-        } else if (!(profileRequestContext.getInboundMessageContext().getMessage() instanceof Response)) {
-            log.info("{} Inbound message was not a SAML Response", getLogPrefix());
-            ActionSupport.buildEvent(profileRequestContext, EventIds.MESSAGE_PROC_ERROR);
+        recordSuccess();
+        
+        if (transcoderRegistry != null) {
+            processAttributes(profileRequestContext);
+        }
+        
+        buildAuthenticationResult(profileRequestContext, authenticationContext);
+        
+        if (authenticationContext.getAuthenticationResult() != null) {
+            if (samlAuthnContext.getAuthnStatement().getAuthnInstant() != null) {
+                authenticationContext.getAuthenticationResult().setAuthenticationInstant(
+                        samlAuthnContext.getAuthnStatement().getAuthnInstant());
+            }
         }
     }
     
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Subject populateSubject(@Nonnull final Subject subject) {
+        
+        final Collection<AuthenticatingAuthority> authorities =
+                samlAuthnContext.getAuthnStatement().getAuthnContext().getAuthenticatingAuthorities();
+        if (!authorities.isEmpty()) {
+            final ProxyAuthenticationPrincipal proxied = new ProxyAuthenticationPrincipal(
+                    authorities
+                        .stream()
+                        .filter(aa -> !Strings.isNullOrEmpty(aa.getURI()))
+                        .map(AuthenticatingAuthority::getURI)
+                        .collect(Collectors.toUnmodifiableList()));
+            subject.getPrincipals().add(proxied);
+        }
+        
+        if (attributeContext != null && !attributeContext.getIdPAttributes().isEmpty()) {
+            log.debug("{} Adding filtered inbound attributes to Subject", getLogPrefix());
+            subject.getPrincipals().addAll(
+                attributeContext.getIdPAttributes().values()
+                    .stream()
+                    .map(a -> new IdPAttributePrincipal(a))
+                    .collect(Collectors.toUnmodifiableList()));
+        }
+        
+        return subject;
+    }
+    
+    /**
+     * Process the inbound SAML Attributes.
+     * 
+     * @param profileRequestContext current profile request context
+     */
+    private void processAttributes(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        log.debug("{} Decoding incoming SAML Attributes", getLogPrefix());
+        
+        final Multimap<String,IdPAttribute> mapped = HashMultimap.create();
+
+        ServiceableComponent<AttributeTranscoderRegistry> component = null;
+        try {
+            component = transcoderRegistry.getServiceableComponent();
+            if (component == null) {
+                log.error("Attribute transcoder service unavailable");
+                return;
+            }
+
+            final Response response = (Response) profileRequestContext.getInboundMessageContext().getMessage();
+            for (final Assertion assertion : response.getAssertions()) {
+                for (final AttributeStatement statement : assertion.getAttributeStatements()) {
+                    for (final Attribute designator : statement.getAttributes()) {
+                        try {
+                            decodeAttribute(component.getComponent(), profileRequestContext, designator, mapped);
+                        } catch (final AttributeDecodingException e) {
+                            log.error("{} Error decoding inbound Attribute", getLogPrefix(), e);
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (component != null) {
+                component.unpinComponent();
+            }
+        }
+                
+        log.debug("{} Incoming SAML Attributes mapped to attribute IDs: {}", getLogPrefix(), mapped.keySet());
+        
+        if (!mapped.isEmpty()) {
+            attributeContext = new AttributeContext();
+            attributeContext.setUnfilteredIdPAttributes(mapped.values());
+            filterAttributes(profileRequestContext);
+        }
+    }
+    
+    /**
+     * Access the registry of transcoding rules to decode the input {@link Attribute}.
+     * 
+     * @param registry  registry of transcoding rules
+     * @param profileRequestContext current profile request context
+     * @param input input object
+     * @param results collection to add results to
+     * 
+     * @throws AttributeDecodingException if an error occurs or no results were obtained
+     */
+    private void decodeAttribute(@Nonnull final AttributeTranscoderRegistry registry,
+            @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final Attribute input,
+            @Nonnull @NonnullElements @Live final Multimap<String,IdPAttribute> results)
+                    throws AttributeDecodingException {
+        
+        final Collection<TranscodingRule> transcodingRules = registry.getTranscodingRules(input);
+        if (transcodingRules.isEmpty()) {
+            log.info("{} No transcoding rule for Attribute '{}'", getLogPrefix(), input.getName());
+            return;
+        }
+        
+        for (final TranscodingRule rules : transcodingRules) {
+            final AttributeTranscoder<Attribute> transcoder = TranscoderSupport.getTranscoder(rules);
+            final IdPAttribute decodedAttribute = transcoder.decode(profileRequestContext, input, rules);
+            if (decodedAttribute != null) {
+                results.put(decodedAttribute.getId(), decodedAttribute);
+            }
+        }
+    }
+    
+    /**
+     * Check for inbound attributes and apply filtering.
+     * 
+     * @param profileRequestContext current profile request context
+     */
+    private void filterAttributes(@Nonnull final ProfileRequestContext profileRequestContext) {
+        if (attributeFilterService == null) {
+            log.warn("{} No AttributeFilter service provided", getLogPrefix());
+            attributeContext.setIdPAttributes(null);
+            return;
+        }
+        
+        
+        final AttributeFilterContext filterContext = samlAuthnContext.getSubcontext(AttributeFilterContext.class, true);
+        
+        populateFilterContext(profileRequestContext, filterContext);
+        
+        ServiceableComponent<AttributeFilter> component = null;
+
+        try {
+            component = attributeFilterService.getServiceableComponent();
+            if (null == component) {
+                log.error("{} Error while filtering inbound attributes: Invalid Attribute Filter configuration",
+                        getLogPrefix());
+                attributeContext.setIdPAttributes(null);
+            } else {
+                final AttributeFilter filter = component.getComponent();
+                filter.filterAttributes(filterContext);
+                filterContext.getParent().removeSubcontext(filterContext);
+                attributeContext.setIdPAttributes(filterContext.getFilteredIdPAttributes().values());
+            }
+        } catch (final AttributeFilterException e) {
+            log.error("{} Error while filtering inbound attributes", getLogPrefix(), e);
+            attributeContext.setIdPAttributes(null);
+        } finally {
+            if (null != component) {
+                component.unpinComponent();
+            }
+        }        
+    }
+    
+    /**
+     * Fill in the filter context data.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param filterContext context to populate
+     */
+    private void populateFilterContext(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AttributeFilterContext filterContext) {
+        
+        filterContext.setDirection(Direction.INBOUND)
+            .setPrefilteredIdPAttributes(attributeContext.getUnfilteredIdPAttributes().values())
+            .setMetadataResolver(metadataResolver)
+            .setRequesterMetadataContextLookupStrategy(null)
+            .setIssuerMetadataContextLookupStrategy(
+                    new SAMLMetadataContextLookupFunction().compose(
+                            new RecursiveTypedParentContextLookup<>(ProfileRequestContext.class)))
+            .setProxiedRequesterContextLookupStrategy(null)
+            .setAttributeIssuerID(
+                    ((Assertion) samlAuthnContext.getAuthnStatement().getParent()).getIssuer().getValue())
+            .setAttributeRecipientID(recipientIdLookupStrategy.apply(profileRequestContext));
+    }
+
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list