[java-identity-provider] branch master updated: IDP-1235 Add support to pe-resolve attributes

Rod Widdowson rdw at steadingsoftware.com
Fri Sep 20 10:35:55 EDT 2019


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

rdw 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=fe0f207baf6e5cdf513ead9aa745e0bd506b53ef

The following commit(s) were added to refs/heads/master by this push:
       new  fe0f207   IDP-1235 Add support to pe-resolve attributes
fe0f207 is described below

commit fe0f207baf6e5cdf513ead9aa745e0bd506b53ef
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Fri Sep 20 11:11:52 2019 +0100

    IDP-1235 Add support to pe-resolve attributes
    
    https://issues.shibboleth.net/jira/browse/IDP-1235
    
    These are placed, for the duration of the resolve, into an AtttributeContext which is the
    child of the AttributeResolutionContext.  Configuration to follow.
---
 .../resolver/impl/AttributeResolverImpl.java       | 75 ++++++++++++++++++++--
 .../resolver/impl/AttributeResolverImplTest.java   | 53 +++++++++++++++
 2 files changed, 123 insertions(+), 5 deletions(-)

diff --git a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
index 99d7911..b1a0b10 100644
--- a/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
+++ b/idp-attribute-resolver-impl/src/main/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImpl.java
@@ -47,6 +47,7 @@ import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
 import net.shibboleth.idp.attribute.EmptyAttributeValue;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.context.AttributeContext;
 import net.shibboleth.idp.attribute.resolver.AttributeDefinition;
 import net.shibboleth.idp.attribute.resolver.AttributeResolver;
 import net.shibboleth.idp.attribute.resolver.DataConnector;
@@ -90,8 +91,11 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
     /** Data connectors defined for this resolver. */
     @NonnullAfterInit private Map<String, DataConnector> dataConnectors;
 
-    /** cache for the log prefix - to save multiple recalculations. */
+    /** Cache for the log prefix - to save multiple recalculations. */
     @NonnullAfterInit private String logPrefix;
+
+    /** PreRequestedAttributes, resolved first and made available for late-comers. */
+    @NonnullAfterInit private List<String> preRequestedAttributes;
   
     /** Whether to strip null attribute values. */
     private boolean stripNulls;
@@ -128,6 +132,13 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
         attributeDefinitions = ImmutableMap.copyOf(checkedDefinitions);
     }
 
+    /** Sets the Attributes to be resolved "first" for this resolver.
+     * @param attrs what to set.
+     */
+    public void setPreRequestedAttributes(final List<String> attrs) {
+        preRequestedAttributes = Collections.unmodifiableList(attrs);
+    }
+
     /**
      * Gets the collection of attribute definitions for this resolver.
      * 
@@ -212,6 +223,7 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
      * 
      * @throws ResolutionException thrown if there is a problem resolving the attributes for the subject
      */
+    // CheckStyle: CyclomaticComplexity OFF
     @Override public void resolveAttributes(@Nonnull final AttributeResolutionContext resolutionContext)
             throws ResolutionException {
         ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
@@ -221,11 +233,20 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
 
         final AttributeResolverWorkContext workContext =
                 resolutionContext.getSubcontext(AttributeResolverWorkContext.class, true);
+        AttributeContext attributeContext = null;
 
         final boolean timerStarted = startTimer(resolutionContext);
         try {
             log.debug("{} Initiating attribute resolution", logPrefix);
 
+            if (!preRequestedAttributes.isEmpty()) {
+                log.debug("Resolving pre-requested Attributes");
+                for (final String attributeId : preRequestedAttributes) {
+                    resolveAttributeDefinition(attributeId, resolutionContext);
+                }
+                attributeContext = finalizePreResolvedAttributes(resolutionContext);
+            }
+
             boolean hasExportingDataConnector = false;
 
             for (final Entry<String, DataConnector> dataConnectorEntry : dataConnectors.entrySet()) {
@@ -256,12 +277,15 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
                     resolutionContext.getResolvedIdPAttributes().keySet());
         } finally {
             resolutionContext.removeSubcontext(workContext);
-
+            if (attributeContext != null) {
+                resolutionContext.removeSubcontext(attributeContext);
+            }
             if (timerStarted) {
                 stopTimer(resolutionContext);
             }
         }
     }
+    // CheckStyle: CyclomaticComplexity ON
 
     /**
      * Gets the list of attributes, identified by IDs, that should be resolved. If the
@@ -477,9 +501,10 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
      * Helper function to collect suitably resolved attributes.
      * @param resolvedAttributes bucket to collect attributes into
      * @param workContext context to extract attributes from
+     * @param includeDependencyOnly whether we include dependencyOnly attributes
      */
     private void collectResolvedAttributes(final Map<String, IdPAttribute> resolvedAttributes,
-            final AttributeResolverWorkContext workContext) {
+            final AttributeResolverWorkContext workContext, final boolean includeDependencyOnly) {
 
         for (final ResolvedAttributeDefinition definition : workContext.getResolvedIdPAttributeDefinitions().values()) {
             final IdPAttribute resolvedAttribute = definition.getResolvedAttribute();
@@ -491,7 +516,7 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
             }
 
             // Remove dependency-only attributes.
-            if (definition.isDependencyOnly()) {
+            if (definition.isDependencyOnly() && !includeDependencyOnly) {
                 log.debug("{} Removing result of attribute definition '{}', is marked as dependency only", logPrefix,
                         definition.getId());
                 continue;
@@ -584,13 +609,43 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
 
         final Map<String, IdPAttribute> resolvedAttributes = new LazyMap<>();
 
-        collectResolvedAttributes(resolvedAttributes, workContext);
+        collectResolvedAttributes(resolvedAttributes, workContext, false) ;
 
         collectExportingDataConnectors(resolvedAttributes, workContext);
 
         resolutionContext.setResolvedIdPAttributes(resolvedAttributes.values());
     }
 
+    /**
+     * Collects the set of pre resolved attributes and places them in an {@link AttributeContext} which inserted
+     * as a child of the  {@link AttributeResolutionContext} and also returned.
+     * <p>
+     * Values are also de-duplicated here.
+     * </p>
+     *
+     * @param resolutionContext current resolution context
+     * @return a populated Attribute Context, or nothing
+     */
+    @Nullable protected AttributeContext finalizePreResolvedAttributes(@Nonnull
+            final AttributeResolutionContext resolutionContext) {
+        Constraint.isNotNull(resolutionContext, "Attribute resolution context cannot be null");
+        final AttributeResolverWorkContext workContext =
+                resolutionContext.getSubcontext(AttributeResolverWorkContext.class, false);
+
+        final Map<String, IdPAttribute> resolvedAttributes = new LazyMap<>();
+
+        collectResolvedAttributes(resolvedAttributes, workContext, true);
+
+        if (resolvedAttributes.isEmpty()) {
+            return null;
+        }
+
+        final AttributeContext context =  resolutionContext.getSubcontext(AttributeContext.class, true);
+        log.debug("Pre-resolved Attributes: {}", resolvedAttributes.keySet());
+        context.setIdPAttributes(resolvedAttributes.values());
+        return context;
+    }
+
     /** {@inheritDoc} */
     @Override protected void doInitialize() throws ComponentInitializationException {
         super.doInitialize();
@@ -605,6 +660,16 @@ public class AttributeResolverImpl extends AbstractServiceableComponent<Attribut
             throw new ComponentInitializationException("No Data Connectors provided");
         }
 
+        if (null == preRequestedAttributes) {
+            preRequestedAttributes = Collections.emptyList();
+        } else {
+            for (final String requested : preRequestedAttributes) {
+                if (!attributeDefinitions.containsKey(requested)) {
+                    throw new ComponentInitializationException("Definition for prerequested attribute '"
+                            + requested + "' not present");
+                }
+            }
+        }
 
         final HashSet<String> dependencyVerifiedPlugins = new HashSet<>();
         for (final DataConnector plugin : dataConnectors.values()) {
diff --git a/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java b/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java
index 5c5b501..ce30405 100644
--- a/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java
+++ b/idp-attribute-resolver-impl/src/test/java/net/shibboleth/idp/attribute/resolver/impl/AttributeResolverImplTest.java
@@ -20,6 +20,7 @@ package net.shibboleth.idp.attribute.resolver.impl;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -42,6 +43,7 @@ import org.testng.annotations.Test;
 
 import net.shibboleth.idp.attribute.EmptyAttributeValue;
 import net.shibboleth.idp.attribute.EmptyAttributeValue.EmptyType;
+import net.shibboleth.idp.attribute.context.AttributeContext;
 import net.shibboleth.idp.attribute.IdPAttribute;
 import net.shibboleth.idp.attribute.IdPAttributeValue;
 import net.shibboleth.idp.attribute.StringAttributeValue;
@@ -55,6 +57,7 @@ import net.shibboleth.idp.attribute.resolver.ResolverAttributeDefinitionDependen
 import net.shibboleth.idp.attribute.resolver.ResolverDataConnectorDependency;
 import net.shibboleth.idp.attribute.resolver.ad.impl.SimpleAttributeDefinition;
 import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolverWorkContext;
 import net.shibboleth.idp.attribute.resolver.dc.impl.StaticDataConnector;
 import net.shibboleth.idp.saml.impl.TestSources;
 import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -238,8 +241,37 @@ public class AttributeResolverImplTest {
         resolver.resolveAttributes(context);
 
         assertTrue(context.getResolvedIdPAttributes().isEmpty());
+        assertNull(context.getSubcontext(AttributeContext.class));
     }
 
+    @Test public void resolvePreRequestAttribute() throws Exception {
+        final IdPAttribute attribute = new IdPAttribute("ad1");
+        attribute.setValues(Collections.singletonList(new StringAttributeValue("value1")));
+
+        final IdPAttribute attribute2 = new IdPAttribute("ad2");
+        attribute2.setValues(Collections.singletonList(new StringAttributeValue("value2")));
+
+        final LazySet<AttributeDefinition> definitions = new LazySet<>();
+        definitions.add(new MockAttributeDefinition("ad1", attribute));
+        definitions.add(new PreDefinedCheckingMockAttributeDefinition("ad2", attribute2, "ad1"));
+        for (AttributeDefinition defn:definitions) {
+            defn.initialize();
+        }
+
+        final AttributeResolverImpl resolver = newAttributeResolverImpl("foo", definitions, null);
+        resolver.setPreRequestedAttributes(List.of("ad1"));
+        resolver.initialize();
+
+        AttributeResolutionContext context = new AttributeResolutionContext();
+        resolver.resolveAttributes(context);
+
+        assertEquals(context.getResolvedIdPAttributes().size(), 2);
+        assertEquals(context.getResolvedIdPAttributes().get("ad1"), attribute);
+        assertEquals(context.getResolvedIdPAttributes().get("ad2"), attribute2);
+        assertNull(context.getSubcontext(AttributeContext.class));
+    }
+
+
     /** Test that a simple resolve returns the expected results. */
     @Test public void resolveFails() throws Exception {
         log.debug("Log Resolve fails");
@@ -897,4 +929,25 @@ public class AttributeResolverImplTest {
         result.setDataConnectors(connectors);
         return result;
     }
+
+    private static class PreDefinedCheckingMockAttributeDefinition extends MockAttributeDefinition {
+        private final String preResolvedName;
+
+        public PreDefinedCheckingMockAttributeDefinition(String id, IdPAttribute value, String preName)
+                throws ComponentInitializationException {
+            super(id, value);
+            preResolvedName = preName;
+        }
+
+        @Override
+        @Nullable protected IdPAttribute doAttributeDefinitionResolve(
+                @Nonnull final AttributeResolutionContext resolutionContext,
+                @Nonnull final AttributeResolverWorkContext workContext) throws ResolutionException {
+            AttributeContext context = resolutionContext.getSubcontext(AttributeContext.class);
+            assertNotNull(context);
+            assertEquals(context.getIdPAttributes().size(), 1);
+            assertTrue(context.getIdPAttributes().containsKey(preResolvedName));
+            return super.doAttributeDefinitionResolve(resolutionContext, workContext);
+        }
+    }
 }

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


More information about the commits mailing list