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

Scott Cantor cantor.2 at osu.edu
Tue Nov 19 20:14:08 EST 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=09ba5c2af7c4753a18b7863d8f92ef48c2ef5131

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

commit 09ba5c2af7c4753a18b7863d8f92ef48c2ef5131
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Nov 19 20:09:12 2019 -0500

    IDP-1494 - Login flow for proxied SAML authentication
    
    https://issues.shibboleth.net/jira/browse/IDP-1494
    
    Encode ProxyRestriction condition into Subject.
    Enforce proxy restrictions generically during finalization.
---
 idp-authn-impl/pom.xml                             |  16 ++-
 .../idp/authn/impl/FinalizeAuthentication.java     |  99 ++++++++++++++--
 .../idp/authn/impl/FinalizeAuthenticationTest.java | 124 +++++++++++++++++++--
 .../profile/impl/ValidateSAMLAuthentication.java   |  56 ++++++++--
 4 files changed, 261 insertions(+), 34 deletions(-)

diff --git a/idp-authn-impl/pom.xml b/idp-authn-impl/pom.xml
index 2e1fba2..106e461 100644
--- a/idp-authn-impl/pom.xml
+++ b/idp-authn-impl/pom.xml
@@ -24,6 +24,11 @@
         <!-- Compile Dependencies -->
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>idp-admin-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>idp-attribute-api</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -163,11 +168,6 @@
             <artifactId>DuoWeb</artifactId>
             <version>${duoweb.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.jasig.cas.client</groupId>
-            <artifactId>cas-client-core</artifactId>
-            <version>${cas-client.version}</version>
-        </dependency>        
         
 
         <!-- DEPRECATED - remove in V5 -->
@@ -197,6 +197,12 @@
 
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-saml-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-security-impl</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java
index 83f7263..53dd433 100644
--- a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/impl/FinalizeAuthentication.java
@@ -22,10 +22,13 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.security.auth.Subject;
 
+import net.shibboleth.idp.admin.AdministrativeFlowDescriptor;
 import net.shibboleth.idp.authn.AbstractAuthenticationAction;
 import net.shibboleth.idp.authn.AuthenticationFlowDescriptor;
 import net.shibboleth.idp.authn.AuthenticationResult;
@@ -37,9 +40,14 @@ import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.authn.principal.PrincipalEvalPredicate;
 import net.shibboleth.idp.authn.principal.PrincipalEvalPredicateFactory;
 import net.shibboleth.idp.authn.principal.PrincipalSupportingComponent;
+import net.shibboleth.idp.authn.principal.ProxyAuthenticationPrincipal;
 import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
 import net.shibboleth.idp.session.context.SessionContext;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
 
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.slf4j.Logger;
@@ -50,6 +58,9 @@ import org.slf4j.LoggerFactory;
  * of an active result) and transfers information from other contexts into a {@link SubjectContext}
  * child of the {@link ProfileRequestContext}.
  * 
+ * <p>The action enforces any constraints on proxying that may be present in the result against
+ * the intended use based on the {@link RelyingPartyContext}.</p>
+ * 
  * <p>The action also cross-checks {@link RequestedPrincipalContext#getMatchingPrincipal()}, if set,
  * against the {@link AuthenticationResult} to ensure that the result produced actually satisfies the
  * request. This is redundant when reusing active results, but is necessary to prevent a flow from running
@@ -75,6 +86,7 @@ import org.slf4j.LoggerFactory;
  * 
  * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
  * @event {@link IdPEventIds#INVALID_SUBJECT_CTX}
+ * @event {@link AuthnEventIds#INVALID_AUTHN_CTX}
  * @event {@link AuthnEventIds#REQUEST_UNSUPPORTED}
  * 
  * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
@@ -90,10 +102,31 @@ public class FinalizeAuthentication extends AbstractAuthenticationAction {
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(FinalizeAuthentication.class);
     
+    /** Strategy used to look up a {@link RelyingPartyContext} for proxy validation. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+    
     /** The principal name extracted from the context tree. */
     @Nullable private String canonicalPrincipalName;
-        
-// Checkstyle: CyclomaticComplexity OFF
+     
+    /** Constructor. */
+    public FinalizeAuthentication() {
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+    }
+    
+    /**
+     * Set the strategy used to return the {@link RelyingPartyContext}.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+    
+    // Checkstyle: CyclomaticComplexity OFF
     /** {@inheritDoc} */
     @Override
     protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
@@ -119,6 +152,15 @@ public class FinalizeAuthentication extends AbstractAuthenticationAction {
                         canonicalPrincipalName);
             }
         }
+
+        final AuthenticationResult latest = authenticationContext.getAuthenticationResult();
+        if (latest == null) {
+            log.warn("{} Authentication result missing from context?", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_AUTHN_CTX);
+            return false;
+        } else if (!checkProxyRestrictions(profileRequestContext, latest.getSubject())) {
+            return false;
+        }
         
         // Check for requested Principal criteria and make sure the result accomodates the criteria.
         // This is required because flow selection is based (generally) on statically-defined information
@@ -126,12 +168,6 @@ public class FinalizeAuthentication extends AbstractAuthenticationAction {
         final RequestedPrincipalContext requestedPrincipalCtx =
                 authenticationContext.getSubcontext(RequestedPrincipalContext.class);
         if (requestedPrincipalCtx != null) {
-            final AuthenticationResult latest = authenticationContext.getAuthenticationResult();
-            if (latest == null) {
-                log.warn("{} Authentication result missing from context?", getLogPrefix());
-                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.REQUEST_UNSUPPORTED);
-                return false;
-            }
             
             // If a matching principal is set, re-verify it. Normally this will work.
             final Principal match = requestedPrincipalCtx.getMatchingPrincipal();
@@ -221,7 +257,7 @@ public class FinalizeAuthentication extends AbstractAuthenticationAction {
                             p.getClass(), requestedPrincipalCtx.getOperator());
             if (factory != null) {
                 final PrincipalEvalPredicate predicate = factory.getPredicate(p);
-
+    
                 // For unweighted results, we'd just apply the predicate to the AuthenticationResult, but
                 // we won't be able to honor weighting, so we have to walk the supported principals one
                 // at a time, wrap it to apply the predicate, and then record it if it succeeds.
@@ -261,4 +297,49 @@ public class FinalizeAuthentication extends AbstractAuthenticationAction {
         return flowDescriptor.getHighestWeighted(matches);
     }
 
+// Checkstyle: CyclomaticComplexity OFF
+    /**
+     * Check for proxy restrictions and evaluate them against the request.
+     * 
+     * @param profileRequestContext current profile request context
+     * @param subject the authentication result's subject
+     * 
+     * @return true iff processing should continue
+     */
+    private boolean checkProxyRestrictions(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final Subject subject) {
+        
+        final Set<ProxyAuthenticationPrincipal> proxieds = subject.getPrincipals(ProxyAuthenticationPrincipal.class);
+        if (proxieds == null || proxieds.isEmpty()) {
+            return true;
+        }
+        
+        // Check for admin flow as relying party.
+        final RelyingPartyContext rpCtx = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+        if (rpCtx == null || rpCtx.getRelyingPartyId() == null) {
+            log.debug("{} No RelyingParty identity, ignoring proxy restrictions on result", getLogPrefix());
+            return true;
+        } else if (rpCtx.getProfileConfig() instanceof AdministrativeFlowDescriptor) {
+            log.debug("{} Relying party is an admin flow, ignoring proxy restrictions on result",
+                    getLogPrefix());
+            return true;
+        }
+        
+        for (final ProxyAuthenticationPrincipal proxied : proxieds) {
+            if (proxied.getProxyCount() != null && proxied.getProxyCount() == 0) {
+                log.warn("{} Result contains a proxy count of zero, disallowing use", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.REQUEST_UNSUPPORTED);
+                return false;
+            } else if (!proxied.getAudiences().isEmpty() &&
+                    !proxied.getAudiences().contains(rpCtx.getRelyingPartyId())) {
+                log.warn("{} Result contains a proxy restriction disallowing relying party '{}'",
+                        getLogPrefix(), rpCtx.getRelyingPartyId());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.REQUEST_UNSUPPORTED);
+                return false;
+            }
+        }
+        
+        return true;
+    }
+// Checkstyle: CyclomaticComplexity ON
 }
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FinalizeAuthenticationTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FinalizeAuthenticationTest.java
index f7cb47f..0bd663c 100644
--- a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FinalizeAuthenticationTest.java
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/impl/FinalizeAuthenticationTest.java
@@ -17,22 +17,24 @@
 
 package net.shibboleth.idp.authn.impl;
 
-import java.security.Principal;
 import java.util.Arrays;
 import java.util.Collections;
 
 import javax.security.auth.Subject;
 
+import net.shibboleth.idp.admin.BasicAdministrativeFlowDescriptor;
 import net.shibboleth.idp.authn.AuthenticationResult;
 import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.idp.authn.context.AuthenticationContext;
 import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
 import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
 import net.shibboleth.idp.authn.context.SubjectContext;
+import net.shibboleth.idp.authn.principal.ProxyAuthenticationPrincipal;
 import net.shibboleth.idp.authn.principal.TestPrincipal;
 import net.shibboleth.idp.authn.principal.impl.ExactPrincipalEvalPredicateFactory;
 import net.shibboleth.idp.profile.ActionTestingSupport;
 import net.shibboleth.idp.profile.IdPEventIds;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
 
 import org.springframework.webflow.execution.Event;
 import org.testng.Assert;
@@ -54,13 +56,19 @@ public class FinalizeAuthenticationTest extends BaseAuthenticationContextTest {
     @Test public void testNotSet() {
         final Event event = action.execute(src);
         
-        ActionTestingSupport.assertProceedEvent(event);
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_AUTHN_CTX);
         Assert.assertNull(prc.getSubcontext(SubjectContext.class));
     }
 
     @Test public void testMismatch() {
         prc.getSubcontext(SubjectContext.class, true).setPrincipalName("foo");
         prc.getSubcontext(SubjectCanonicalizationContext.class, true).setPrincipalName("bar");
+
+        final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        active.getSubject().getPrincipals().add(new TestPrincipal("bar2"));
+        
+        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        authCtx.setAuthenticationResult(active);
         
         final Event event = action.execute(src);
         
@@ -81,7 +89,7 @@ public class FinalizeAuthenticationTest extends BaseAuthenticationContextTest {
                 TestPrincipal.class, "florp", new ExactPrincipalEvalPredicateFactory());
         rpCtx.setMatchingPrincipal(new TestPrincipal("bar1"));
         rpCtx.setOperator("florp");
-        rpCtx.setRequestedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("bar1")));
+        rpCtx.setRequestedPrincipals(Collections.singletonList(new TestPrincipal("bar1")));
         authCtx.addSubcontext(rpCtx);
         
         final Event event = action.execute(src);
@@ -103,7 +111,7 @@ public class FinalizeAuthenticationTest extends BaseAuthenticationContextTest {
                 TestPrincipal.class, "florp", new ExactPrincipalEvalPredicateFactory());
         rpCtx.setMatchingPrincipal(new TestPrincipal("bar1"));
         rpCtx.setOperator("florp");
-        rpCtx.setRequestedPrincipals(Collections.<Principal>singletonList(new TestPrincipal("bar2")));
+        rpCtx.setRequestedPrincipals(Collections.singletonList(new TestPrincipal("bar2")));
         authCtx.addSubcontext(rpCtx);
         
         final Event event = action.execute(src);
@@ -121,14 +129,14 @@ public class FinalizeAuthenticationTest extends BaseAuthenticationContextTest {
         
         final Event event = action.execute(src);
         
-        ActionTestingSupport.assertProceedEvent(event);
-        SubjectContext sc = prc.getSubcontext(SubjectContext.class);
-        Assert.assertNotNull(sc);
-        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.INVALID_AUTHN_CTX);
+        Assert.assertNull(prc.getSubcontext(SubjectContext.class));
     }
 
     @Test public void testOneActive() {
         final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        active.getSubject().getPrincipals().add(
+                new ProxyAuthenticationPrincipal(Collections.singletonList(ActionTestingSupport.OUTBOUND_MSG_ISSUER)));
         final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
         authCtx.setActiveResults(Arrays.asList(active));
         authCtx.setAuthenticationResult(active);
@@ -159,4 +167,104 @@ public class FinalizeAuthenticationTest extends BaseAuthenticationContextTest {
         Assert.assertEquals(sc.getPrincipalName(), "foo");
         Assert.assertEquals(sc.getAuthenticationResults().size(), 2);
     }
+
+    @Test public void testZeroProxyCount() {
+        final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        final ProxyAuthenticationPrincipal proxy =
+                new ProxyAuthenticationPrincipal(Collections.singletonList(ActionTestingSupport.OUTBOUND_MSG_ISSUER));
+        proxy.setProxyCount(0);
+        active.getSubject().getPrincipals().add(proxy);
+        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        authCtx.setActiveResults(Arrays.asList(active));
+        authCtx.setAuthenticationResult(active);
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setPrincipalName("foo");
+
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.REQUEST_UNSUPPORTED);
+        Assert.assertNull(prc.getSubcontext(SubjectContext.class));
+    }
+
+    @Test public void testZeroProxyCountAdminFlow() {
+        final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        final ProxyAuthenticationPrincipal proxy =
+                new ProxyAuthenticationPrincipal(Collections.singletonList(ActionTestingSupport.OUTBOUND_MSG_ISSUER));
+        proxy.setProxyCount(0);
+        active.getSubject().getPrincipals().add(proxy);
+        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        authCtx.setActiveResults(Arrays.asList(active));
+        authCtx.setAuthenticationResult(active);
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setPrincipalName("foo");
+
+        prc.getSubcontext(RelyingPartyContext.class).setProfileConfig(new BasicAdministrativeFlowDescriptor("admin/test"));
+
+        final Event event = action.execute(src);
+
+        ActionTestingSupport.assertProceedEvent(event);
+        SubjectContext sc = prc.getSubcontext(SubjectContext.class);
+        Assert.assertNotNull(sc);
+        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(sc.getAuthenticationResults().size(), 1);
+    }
+
+    @Test public void testZeroProxyCountNoRP() {
+        final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        final ProxyAuthenticationPrincipal proxy =
+                new ProxyAuthenticationPrincipal(Collections.singletonList(ActionTestingSupport.OUTBOUND_MSG_ISSUER));
+        proxy.setProxyCount(0);
+        active.getSubject().getPrincipals().add(proxy);
+        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        authCtx.setActiveResults(Arrays.asList(active));
+        authCtx.setAuthenticationResult(active);
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setPrincipalName("foo");
+
+        prc.removeSubcontext(RelyingPartyContext.class);
+
+        final Event event = action.execute(src);
+
+        ActionTestingSupport.assertProceedEvent(event);
+        SubjectContext sc = prc.getSubcontext(SubjectContext.class);
+        Assert.assertNotNull(sc);
+        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(sc.getAuthenticationResults().size(), 1);
+    }
+
+    @Test public void testValidProxyAudience() {
+        final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        final ProxyAuthenticationPrincipal proxy =
+                new ProxyAuthenticationPrincipal(Collections.singletonList(ActionTestingSupport.OUTBOUND_MSG_ISSUER));
+        proxy.setProxyCount(10);
+        proxy.getAudiences().add(ActionTestingSupport.INBOUND_MSG_ISSUER);
+        active.getSubject().getPrincipals().add(proxy);
+        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        authCtx.setActiveResults(Arrays.asList(active));
+        authCtx.setAuthenticationResult(active);
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setPrincipalName("foo");
+
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertProceedEvent(event);
+        SubjectContext sc = prc.getSubcontext(SubjectContext.class);
+        Assert.assertNotNull(sc);
+        Assert.assertEquals(sc.getPrincipalName(), "foo");
+        Assert.assertEquals(sc.getAuthenticationResults().size(), 1);
+    }
+
+    @Test public void testInvalidProxyAudience() {
+        final AuthenticationResult active = new AuthenticationResult("test2", new Subject());
+        final ProxyAuthenticationPrincipal proxy =
+                new ProxyAuthenticationPrincipal(Collections.singletonList(ActionTestingSupport.OUTBOUND_MSG_ISSUER));
+        proxy.setProxyCount(10);
+        proxy.getAudiences().add(ActionTestingSupport.OUTBOUND_MSG_ISSUER);
+        active.getSubject().getPrincipals().add(proxy);
+        final AuthenticationContext authCtx = prc.getSubcontext(AuthenticationContext.class);
+        authCtx.setActiveResults(Arrays.asList(active));
+        authCtx.setAuthenticationResult(active);
+        prc.getSubcontext(SubjectCanonicalizationContext.class, true).setPrincipalName("foo");
+
+        final Event event = action.execute(src);
+        
+        ActionTestingSupport.assertEvent(event, AuthnEventIds.REQUEST_UNSUPPORTED);
+        Assert.assertNull(prc.getSubcontext(SubjectContext.class));
+    }
+
 }
\ 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 391213b..390d1ac 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
@@ -67,8 +67,10 @@ 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.Audience;
 import org.opensaml.saml.saml2.core.AuthenticatingAuthority;
 import org.opensaml.saml.saml2.core.AuthnContext;
+import org.opensaml.saml.saml2.core.ProxyRestriction;
 import org.opensaml.saml.saml2.core.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -291,7 +293,8 @@ public class ValidateSAMLAuthentication extends AbstractValidationAction {
             }
         }
     }
-    
+        
+// Checkstyle: CyclomaticComplexity OFF
     /** {@inheritDoc} */
     @Override
     @Nonnull protected Subject populateSubject(@Nonnull final Subject subject) {
@@ -327,9 +330,35 @@ public class ValidateSAMLAuthentication extends AbstractValidationAction {
             log.warn("{} No AuthnContext information usable from assertion", getLogPrefix());
         }
         
+        subject.getPrincipals().add(buildProxyPrincipal(authnContext));
+        
+        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;
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+    /**
+     * Construct a populated {@link ProxyAuthenticationPrincipal} based on the inbound assertion.
+     * 
+     * @param authnContext the SAML {@link AuthnContext} issued by the proxied IdP
+     * 
+     * @return a constructed {@link ProxyAuthenticationPrincipal} to include in the {@link Subject}
+     */
+    @Nonnull private ProxyAuthenticationPrincipal buildProxyPrincipal(@Nonnull final AuthnContext authnContext) {
+        
         final ProxyAuthenticationPrincipal proxied = new ProxyAuthenticationPrincipal();
-        proxied.getAuthorities().add(
-                ((Assertion) samlAuthnContext.getAuthnStatement().getParent()).getIssuer().getValue());
+        
+        final Assertion assertion = (Assertion) samlAuthnContext.getAuthnStatement().getParent();
+        
+        proxied.getAuthorities().add(assertion.getIssuer().getValue());
         
         if (!authnContext.getAuthenticatingAuthorities().isEmpty()) {
             proxied.getAuthorities().addAll(
@@ -339,18 +368,21 @@ public class ValidateSAMLAuthentication extends AbstractValidationAction {
                         .filter(aa -> !Strings.isNullOrEmpty(aa))
                         .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()));
+        final ProxyRestriction condition = assertion.getConditions().getProxyRestriction();
+        if (condition != null) {
+            proxied.setProxyCount(condition.getProxyCount());
+            if (condition.getAudiences() != null) {
+                proxied.getAudiences().addAll(
+                        condition.getAudiences()
+                            .stream()
+                            .map(Audience::getURI)
+                            .filter(a -> !Strings.isNullOrEmpty(a))
+                            .collect(Collectors.toUnmodifiableList()));
+            }
         }
         
-        return subject;
+        return proxied;
     }
     
     /**

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


More information about the commits mailing list