[java-identity-provider] branch main updated: Implement deferred class for Subject DataConnector and add unit tests.

Scott Cantor cantor.2 at osu.edu
Mon Jul 11 20:19:51 UTC 2022


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

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

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

The following commit(s) were added to refs/heads/main by this push:
     new fa68b126b Implement deferred class for Subject DataConnector and add unit tests.
fa68b126b is described below

commit fa68b126bde727a63b37f6365dcd53ce1d3ca097
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jul 11 16:19:48 2022 -0400

    Implement deferred class for Subject DataConnector and add unit tests.
---
 .../impl/SubjectDerivedAttributesFunction.java     | 178 ++++++++++++++++++++
 .../impl/SubjectDataConnectorParserTest.java       |  53 ++++++
 .../context/impl/SubjectDataConnectorTest.java     | 183 +++++++++++++++++++++
 .../resolver/spring/dc/subjectAttributes.xml       |   7 +
 .../resolver/spring/dc/subjectAttributesNull.xml   |   6 +
 .../.settings/org.eclipse.wst.validation.prefs     |   2 +
 6 files changed, 429 insertions(+)

diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributesFunction.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributesFunction.java
new file mode 100644
index 000000000..5254b9495
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributesFunction.java
@@ -0,0 +1,178 @@
+/*
+ * 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.authn.context.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+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.attribute.IdPAttribute;
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.authn.context.SubjectContext;
+import net.shibboleth.idp.authn.context.navigate.SubjectCanonicalizationContextSubjectLookupFunction;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
+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.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Function which returns {@link IdPAttribute}s derived from the {@link java.security.Principal}s
+ * associated with the request.
+ */
+public class SubjectDerivedAttributesFunction extends AbstractIdentifiableInitializableComponent implements
+        Function<ProfileRequestContext,List<IdPAttribute>> {
+
+    /** Logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SubjectDerivedAttributesFunction.class);
+
+    /** Flag denoting whether plugin is being used for subject c14n or standard usage. */
+    private boolean forCanonicalization;
+    
+    /** Strategy used to locate the {@link SubjectContext} to use. */
+    @Nonnull private Function<ProfileRequestContext,SubjectContext> scLookupStrategy;
+
+    /** Strategy used to locate the {@link Subject} to use. */
+    @Nullable private Function<ProfileRequestContext,Subject> subjectLookupStrategy;
+
+    /** Constructor. */
+    public SubjectDerivedAttributesFunction() {
+        scLookupStrategy = new ChildContextLookup<>(SubjectContext.class);
+    }
+    
+    /**
+     * Gets whether the definition is being used during Subject Canonicalization, causing
+     * auto-installation of an alternate Subject lookup strategy.
+     * 
+     * @return whether the definition is being used during Subject Canonicalization
+     */
+    public boolean isForCanonicalization() {
+        return forCanonicalization;
+    }
+    
+    /**
+     * Sets whether the definition is being used during Subject Canonicalization, causing
+     * auto-installation of an alternate Subject lookup strategy.
+     * 
+     * @param flag flag to set
+     */
+    public void setForCanonicalization(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        forCanonicalization = flag;
+    }
+
+    /**
+     * Sets the strategy used to locate the {@link SubjectContext} associated with a given
+     * {@link net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext}.
+     * 
+     * @param strategy strategy used to locate the {@link SubjectContext} associated with a given
+     *            {@link net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext}
+     */
+    public void setSubjectContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,SubjectContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        scLookupStrategy = Constraint.isNotNull(strategy, "SubjectContext lookup strategy cannot be null");
+    }
+
+    /**
+     * Sets the strategy used to locate a {@link Subject} associated with a given
+     * {@link net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext}.
+     * 
+     * @param strategy strategy used to locate a {@link Subject} associated with a given
+     *            {@link net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext}
+     */
+    public void setSubjectLookupStrategy(@Nullable final Function<ProfileRequestContext,Subject> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        subjectLookupStrategy = strategy;
+    }
+    
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (forCanonicalization && subjectLookupStrategy == null) {
+            log.debug("{} Marked for use during canonicalication, auto-installing Subject lookup strategy",
+                    getLogPrefix());
+            subjectLookupStrategy = new SubjectCanonicalizationContextSubjectLookupFunction().compose(
+                    new ChildContextLookup<>(SubjectCanonicalizationContext.class));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public List<IdPAttribute> apply(@Nullable final ProfileRequestContext prc) {
+        
+        Collection<Subject> subjects = Collections.emptyList();
+        
+        if (subjectLookupStrategy != null) {
+            final Subject subject = subjectLookupStrategy.apply(prc);
+            if (subject == null) {
+                log.debug("{} No Subject returned from lookup, no attribute resolved", getLogPrefix());
+                return null;
+            }
+            subjects = Collections.singletonList(subject);
+        } else {
+            final SubjectContext cs = scLookupStrategy.apply(prc);
+            if (cs == null || cs.getSubjects().isEmpty()) {
+                log.debug("{} No Subjects returned from SubjectContext lookup, no attribute resolved", getLogPrefix());
+                return null;
+            }
+            subjects = cs.getSubjects();
+        }
+        
+        final List<IdPAttribute> results = new ArrayList<>();
+
+        for (final Subject subject : subjects) {
+            results.addAll(subject.getPrincipals(IdPAttributePrincipal.class).stream()
+                    .map(IdPAttributePrincipal::getAttribute)
+                    .collect(Collectors.toUnmodifiableList()));
+        }
+        
+        if (results.isEmpty()) {
+            log.info("{} No attributes resolved", getLogPrefix());
+            return null;
+        }
+        log.debug("{} Generated {} attributes", getLogPrefix(), results.size());
+        
+        return results;
+    }
+
+    /**
+     * Produce a consistent log prefix.
+     * 
+     * @return a  consistent log prefix
+     */
+    private String getLogPrefix() {
+        return "SubjectDerivedDataConnector " + getId();
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDataConnectorParserTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDataConnectorParserTest.java
new file mode 100644
index 000000000..6bd4cf379
--- /dev/null
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDataConnectorParserTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.authn.context.impl;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.resolver.dc.impl.ContextDerivedDataConnector;
+import net.shibboleth.idp.attribute.resolver.spring.dc.impl.SubjectDataConnectorParser;
+import net.shibboleth.idp.attribute.resolver.spring.testing.BaseAttributeDefinitionParserTest;
+
+/**
+ * test for {@link SubjectDataConnectorParser}
+ */
+ at SuppressWarnings("javadoc")
+public class SubjectDataConnectorParserTest extends BaseAttributeDefinitionParserTest {
+    
+    @Test public void simple() {
+        final ContextDerivedDataConnector connector = getDataConnector("resolver/subjectAttributes.xml", ContextDerivedDataConnector.class);
+
+        assertEquals(connector.getExportAttributes().size(), 2);
+        assertTrue(connector.getExportAttributes().contains("foo"));
+        assertTrue(connector.getExportAttributes().contains("bar"));
+        assertTrue(connector.isNoResultIsError());
+        
+        final SubjectDerivedAttributesFunction fn = (SubjectDerivedAttributesFunction) connector.getAttributesFunction();
+        
+        assertTrue(fn.isForCanonicalization());
+    }
+    
+    @Test(expectedExceptions = {BeanCreationException.class}) public void emptyNoResultIsError() {
+        getDataConnector("resolver/subjectAttributesNull.xml", ContextDerivedDataConnector.class);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDataConnectorTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDataConnectorTest.java
new file mode 100644
index 000000000..42e07866a
--- /dev/null
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDataConnectorTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.authn.context.impl;
+
+import static org.testng.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.dc.impl.ContextDerivedDataConnector;
+import net.shibboleth.idp.attribute.resolver.testing.TestSources;
+import net.shibboleth.idp.authn.AuthenticationResult;
+import net.shibboleth.idp.authn.context.SubjectCanonicalizationContext;
+import net.shibboleth.idp.authn.context.SubjectContext;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
+import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Test for {@link SubjectDataConnector}. */
+ at SuppressWarnings("javadoc")
+public class SubjectDataConnectorTest {
+
+    /** Simple result. */
+    private static final String SIMPLE_VALUE = "simple";
+    
+    @Test public void simpleValue() throws ComponentInitializationException, ResolutionException {
+        final List<IdPAttributeValue> list = new ArrayList<>(2);
+        list.add(new StringAttributeValue(SIMPLE_VALUE));
+        list.add(new StringAttributeValue(SIMPLE_VALUE + "2"));
+        
+        final IdPAttribute attr = new IdPAttribute("wibble");
+        attr.setValues(list);
+
+        final SubjectDerivedAttributesFunction fn = new SubjectDerivedAttributesFunction();
+        fn.setId("test");
+        fn.initialize();
+        
+        final ContextDerivedDataConnector defn = new ContextDerivedDataConnector();
+        defn.setAttributesFunction(fn);
+        defn.setId("pDAD");
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        final SubjectContext sc = ctx.getParent().getSubcontext(SubjectContext.class, true);
+        final Map<String, AuthenticationResult> authnResults = sc.getAuthenticationResults();
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new IdPAttributePrincipal(attr));
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+        authnResults.put("one", new AuthenticationResult("1", subject));
+        
+        
+        final Map<String,IdPAttribute> results = defn.resolve(ctx);
+        
+        assertEquals(1, results.size());
+        
+        final IdPAttribute copy = results.get("wibble");
+        
+        assertEquals(copy.getValues().size(), 2);
+        assertTrue(copy.getValues().contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(copy.getValues().contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+
+    @Test public void simpleValueViaC14N() throws ComponentInitializationException, ResolutionException {
+        final List<IdPAttributeValue> list = new ArrayList<>(2);
+        list.add(new StringAttributeValue(SIMPLE_VALUE));
+        list.add(new StringAttributeValue(SIMPLE_VALUE + "2"));
+        
+        final IdPAttribute attr = new IdPAttribute("wibble");
+        attr.setValues(list);
+
+        final SubjectDerivedAttributesFunction fn = new SubjectDerivedAttributesFunction();
+        fn.setId("test");
+        fn.setForCanonicalization(true);
+        fn.initialize();
+
+        final ContextDerivedDataConnector defn = new ContextDerivedDataConnector();
+        defn.setId("pDAD");
+        defn.setAttributesFunction(fn);
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        final SubjectCanonicalizationContext sc = ctx.getParent().getSubcontext(SubjectCanonicalizationContext.class, true);
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new IdPAttributePrincipal(attr));
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+        sc.setSubject(subject);
+        
+        
+        final Map<String,IdPAttribute> results = defn.resolve(ctx);
+        
+        assertEquals(1, results.size());
+        
+        final IdPAttribute copy = results.get("wibble");
+        
+        assertEquals(copy.getValues().size(), 2);
+        assertTrue(copy.getValues().contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(copy.getValues().contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+
+    @SuppressWarnings("removal")
+    @Test public void emptyOk() throws ComponentInitializationException, ResolutionException {
+
+        final SubjectDerivedAttributesFunction fn = new SubjectDerivedAttributesFunction();
+        fn.setId("test");
+        fn.setForCanonicalization(true);
+        fn.initialize();
+
+        final ContextDerivedDataConnector defn = new ContextDerivedDataConnector();
+        defn.setExportAllAttributes(true);
+        defn.setId("pDAD");
+        defn.setAttributesFunction(fn);
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        final SubjectContext sc = ctx.getParent().getSubcontext(SubjectContext.class, true);
+        final Map<String, AuthenticationResult> authnResults = sc.getAuthenticationResults();
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+        authnResults.put("one", new AuthenticationResult("1", subject));
+        
+        final Map<String,IdPAttribute> results = defn.resolve(ctx);
+        assertNull(results);
+    }
+
+    @SuppressWarnings("removal")
+    @Test(expectedExceptions=ResolutionException.class)
+    public void emptyError() throws ComponentInitializationException, ResolutionException {
+
+        final SubjectDerivedAttributesFunction fn = new SubjectDerivedAttributesFunction();
+        fn.setId("test");
+        fn.initialize();
+
+        final ContextDerivedDataConnector defn = new ContextDerivedDataConnector();
+        defn.setId("pDAD");
+        defn.setExportAllAttributes(true);
+        defn.setNoResultIsError(true);
+        defn.setAttributesFunction(fn);
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);
+        final SubjectContext sc = ctx.getParent().getSubcontext(SubjectContext.class, true);
+        final Map<String, AuthenticationResult> authnResults = sc.getAuthenticationResults();
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+        authnResults.put("one", new AuthenticationResult("1", subject));
+        
+        defn.resolve(ctx);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/subjectAttributes.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/subjectAttributes.xml
new file mode 100644
index 000000000..73f757ef9
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/subjectAttributes.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DataConnector id="subjectAttributes" xsi:type="Subject"
+    xmlns="urn:mace:shibboleth:2.0:resolver"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    exportAttributes="foo bar"
+    noResultIsError="true"
+    forCanonicalization="1"
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd" />
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/subjectAttributesNull.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/subjectAttributesNull.xml
new file mode 100644
index 000000000..e2f5e1203
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/dc/subjectAttributesNull.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DataConnector id="subjectAttributes" xsi:type="Subject"
+    xmlns="urn:mace:shibboleth:2.0:resolver"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    exportAttributes="foo bar"
+    noResultIsError=" "
+	xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd" />
diff --git a/idp-spring/.settings/org.eclipse.wst.validation.prefs b/idp-spring/.settings/org.eclipse.wst.validation.prefs
new file mode 100644
index 000000000..04cad8cb7
--- /dev/null
+++ b/idp-spring/.settings/org.eclipse.wst.validation.prefs
@@ -0,0 +1,2 @@
+disabled=06target
+eclipse.preferences.version=1

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


More information about the commits mailing list