[java-identity-provider] branch main updated: Relocate subject-sourced IdPAttribute functions and unit tests.

Scott Cantor cantor.2 at osu.edu
Mon Jul 11 15:21:34 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=7ceb0b18f68a2a4e3f46e8dd72d623a431b990fc

The following commit(s) were added to refs/heads/main by this push:
     new 7ceb0b18f Relocate subject-sourced IdPAttribute functions and unit tests.
7ceb0b18f is described below

commit 7ceb0b18f68a2a4e3f46e8dd72d623a431b990fc
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jul 11 11:21:31 2022 -0400

    Relocate subject-sourced IdPAttribute functions and unit tests.
---
 idp-authn-impl/pom.xml                             |  26 +++
 .../impl/IdPAttributePrincipalValuesFunction.java  |  76 +++++++
 .../SubjectDerivedAttributeValuesFunction.java     | 193 ++++++++++++++++++
 ...ontextDerivedAttributeDefinitionParserTest.java | 147 ++++++++++++++
 .../SubjectDerivedAttributeValuesFunctionTest.java | 226 +++++++++++++++++++++
 .../resolver/spring/ad/contextDerived.xml          |   5 +
 .../resolver/spring/ad/subjectDerived.xml          |   9 +
 .../resolver/spring/ad/subjectDerivedComplex.xml   |   5 +
 .../spring/ad/subjectDerivedDependency.xml         |  10 +
 .../resolver/spring/ad/subjectDerivedFail.xml      |   9 +
 .../resolver/spring/ad/subjectDerivedViaC14N.xml   |   9 +
 .../resolver/spring/ad/subjectDerivedWarn.xml      |   8 +
 .../resolver/spring/contextDerivedBeans.xml        |  20 ++
 .../idp/attribute/resolver/spring/customBean.xml   |  50 +++++
 idp-spring/.classpath                              |   2 +
 15 files changed, 795 insertions(+)

diff --git a/idp-authn-impl/pom.xml b/idp-authn-impl/pom.xml
index 286f9bd7f..a5fa79154 100644
--- a/idp-authn-impl/pom.xml
+++ b/idp-authn-impl/pom.xml
@@ -241,6 +241,32 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-attribute-resolver-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-attribute-resolver-api</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-attribute-resolver-spring</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>net.shibboleth</groupId>
+            <artifactId>shib-attribute-resolver-spring</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-core</artifactId>
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/IdPAttributePrincipalValuesFunction.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/IdPAttributePrincipalValuesFunction.java
new file mode 100644
index 000000000..d6c10b7ef
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/IdPAttributePrincipalValuesFunction.java
@@ -0,0 +1,76 @@
+/*
+ * 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.security.Principal;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/** Engine to mine values from {@link IdPAttributePrincipal}s. */
+public class IdPAttributePrincipalValuesFunction extends AbstractInitializableComponent implements
+        Function<Principal, List<IdPAttributeValue>> {
+
+    /** The Attribute Name to look for. */
+    @NonnullAfterInit @NotEmpty private String attributeName;
+
+    /**
+     * Set the attribute name.
+     * 
+     * @param attrName the attribute name to read values from
+     */
+    public void setAttributeName(@Nonnull @NotEmpty final String attrName) {
+        attributeName = Constraint.isNotNull(StringSupport.trimOrNull(attrName),
+                "Attribute Name cannot be null or empty");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (attributeName == null) {
+            throw new ComponentInitializationException("Attribute name cannot be null or empty");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public List<IdPAttributeValue> apply(@Nullable final Principal principal) {
+
+        if (null != principal && principal instanceof IdPAttributePrincipal) {
+            final IdPAttributePrincipal attributePrincipal = (IdPAttributePrincipal) principal;
+            final IdPAttribute attribute = attributePrincipal.getAttribute();
+            if (attributeName.equals(attribute.getId())) {
+                return attribute.getValues();
+            }
+        }
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributeValuesFunction.java b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributeValuesFunction.java
new file mode 100644
index 000000000..c43b25218
--- /dev/null
+++ b/idp-authn-impl/src/main/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributeValuesFunction.java
@@ -0,0 +1,193 @@
+/*
+ * 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.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.security.auth.Subject;
+
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+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.utilities.java.support.annotation.constraint.NonnullAfterInit;
+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 IdPAttributeValue}s derived from the {@link java.security.Principal}s
+ * associated with the request. The precise values are determined by an injected {@link Function}.
+ */
+public class SubjectDerivedAttributeValuesFunction extends AbstractIdentifiableInitializableComponent implements
+        Function<ProfileRequestContext,List<IdPAttributeValue>> {
+
+    /** Logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SubjectDerivedAttributeValuesFunction.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;
+
+    /**
+     * {@link Function} used to generate the values associated with a {@link Principal}
+     * 
+     * The {@link Function} returns null or an empty list if the {@link Principal} isn't relevant.
+     */
+    @NonnullAfterInit private Function<Principal,List<IdPAttributeValue>> attributeValuesFunction;
+
+    /** Strategy used to locate the {@link Subject} to use. */
+    @Nullable private Function<ProfileRequestContext,Subject> subjectLookupStrategy;
+
+    /** Constructor. */
+    public SubjectDerivedAttributeValuesFunction() {
+        scLookupStrategy = new ChildContextLookup<>(SubjectContext.class);
+    }
+    
+    /**
+     * 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 function to extract attribute values from a {@link Principal}.
+     * 
+     * @param strategy strategy function
+     */
+    public void setAttributeValuesFunction(@Nonnull final Function<Principal,List<IdPAttributeValue>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        attributeValuesFunction = Constraint.isNotNull(strategy, "Attribute value 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 (attributeValuesFunction == null) {
+            throw new ComponentInitializationException("Attribute value lookup strategy cannot be null");
+        }
+        
+        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<IdPAttributeValue> 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<IdPAttributeValue> results = new ArrayList<>();
+
+        for (final Subject subject : subjects) {
+            for (final Principal principal : subject.getPrincipals()) {
+                final List<IdPAttributeValue> values = attributeValuesFunction.apply(principal);
+                if (null != values && !values.isEmpty()) {
+                    results.addAll(values);
+                }
+            }
+        }
+        if (results.isEmpty()) {
+            log.info("{} Generated no values, no attribute resolved", getLogPrefix());
+            return null;
+        }
+        log.debug("{} Generated {} values", getLogPrefix(), results.size());
+        log.trace("{} Values:", getLogPrefix(), results);
+        return results;
+    }
+
+    /**
+     * Produce a consistent log prefix.
+     * 
+     * @return a  consistent log prefix
+     */
+    private String getLogPrefix() {
+        return "SubjectDerivedAttributeDefinition " + getId();
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/ContextDerivedAttributeDefinitionParserTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/ContextDerivedAttributeDefinitionParserTest.java
new file mode 100644
index 000000000..dd3a4a118
--- /dev/null
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/ContextDerivedAttributeDefinitionParserTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+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.AttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.idp.attribute.resolver.ad.impl.ContextDerivedAttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+import net.shibboleth.idp.attribute.resolver.spring.ad.impl.ContextDerivedAttributeDefinitionParser;
+import net.shibboleth.idp.attribute.resolver.spring.ad.impl.SubjectDerivedAttributeDefinitionParser;
+import net.shibboleth.idp.attribute.resolver.spring.testing.BaseAttributeDefinitionParserTest;
+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;
+
+/**
+ * Test for {@link SubjectDerivedAttributeDefinitionParser} and {@link ContextDerivedAttributeDefinitionParser}.
+ */
+ at SuppressWarnings("javadoc")
+public class ContextDerivedAttributeDefinitionParserTest extends BaseAttributeDefinitionParserTest {
+
+    /** Simple result. */
+    private static final String SIMPLE_VALUE = "simple";
+
+    private AttributeResolutionContext getCtx(final String attributeName, boolean c14n) {
+        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(attributeName);
+        attr.setValues(list);
+
+        final AttributeResolutionContext ctx = TestSources.createResolutionContext(TestSources.PRINCIPAL_ID,
+                TestSources.IDP_ENTITY_ID, TestSources.SP_ENTITY_ID);
+        final Subject subject = new Subject();
+        subject.getPrincipals().add(new IdPAttributePrincipal(attr));
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+
+        if (c14n) {
+            ctx.getParent().getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+        } else {
+            final SubjectContext sc = ctx.getParent().getSubcontext(SubjectContext.class, true);
+            final Map<String, AuthenticationResult> authnResults = sc.getAuthenticationResults();
+            authnResults.put("one", new AuthenticationResult("1", subject));
+        }
+
+        return ctx;
+    }
+
+    @Test public void resolverSubject() throws ResolutionException {
+        final AttributeDefinition attrDef =
+                getAttributeDefn("subjectDerived.xml", ContextDerivedAttributeDefinition.class);
+
+        final List<IdPAttributeValue> foo = attrDef.resolve(getCtx("Whatever", false)).getValues();
+
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+
+    }
+
+    @Test public void resolverSubjectViaC14N() throws ResolutionException {
+        final AttributeDefinition attrDef =
+                getAttributeDefn("subjectDerivedViaC14N.xml", ContextDerivedAttributeDefinition.class);
+
+        final List<IdPAttributeValue> foo = attrDef.resolve(getCtx("Whatever", true)).getValues();
+
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+
+    }
+
+    @Test public void complex() throws ResolutionException {
+        final AttributeDefinition attrDef = getAttributeDefn("subjectDerivedComplex.xml", "contextDerivedBeans.xml",
+                ContextDerivedAttributeDefinition.class);
+
+        final List<IdPAttributeValue> foo = attrDef.resolve(getCtx("BeanWhatever", false)).getValues();
+
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+
+    @Test public void context() throws ResolutionException {
+        final AttributeDefinition attrDef = getAttributeDefn("contextDerived.xml", "contextDerivedBeans.xml",
+                ContextDerivedAttributeDefinition.class);
+
+        final List<IdPAttributeValue> foo = attrDef.resolve(getCtx("BeanWhatever", false)).getValues();
+
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+
+    @Test public void warn() throws ResolutionException {
+        final AttributeDefinition attrDef =
+                getAttributeDefn("subjectDerivedWarn.xml", ContextDerivedAttributeDefinition.class);
+        final List<IdPAttributeValue> foo = attrDef.resolve(getCtx("Whatever", false)).getValues();
+
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+
+    @Test(expectedExceptions = {BeanDefinitionStoreException.class}) public void fail() throws ResolutionException {
+        getAttributeDefn("subjectDerivedFail.xml", ContextDerivedAttributeDefinition.class);
+    }
+
+    @Test(expectedExceptions = {BeanDefinitionStoreException.class}) public void dependency()
+            throws ResolutionException {
+        getAttributeDefn("subjectDerivedDependency.xml", ContextDerivedAttributeDefinition.class);
+    }
+}
diff --git a/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributeValuesFunctionTest.java b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributeValuesFunctionTest.java
new file mode 100644
index 000000000..b97d851d2
--- /dev/null
+++ b/idp-authn-impl/src/test/java/net/shibboleth/idp/authn/context/impl/SubjectDerivedAttributeValuesFunctionTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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.ad.impl.ContextDerivedAttributeDefinition;
+import net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext;
+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;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
+
+/** Test for {@link SubjectDerivedAttributeValuesFunction}. */
+ at SuppressWarnings("javadoc")
+public class SubjectDerivedAttributeValuesFunctionTest {
+
+    /** Simple result. */
+    private static final String SIMPLE_VALUE = "simple";
+    
+
+    @Test public void noSubjectContext() throws ComponentInitializationException, ResolutionException {
+
+        final SubjectDerivedAttributeValuesFunction ctxValueFunction = new SubjectDerivedAttributeValuesFunction();
+        ctxValueFunction.setId("pDaD");
+        final IdPAttributePrincipalValuesFunction fn = new IdPAttributePrincipalValuesFunction();
+        fn.setAttributeName("wibble");
+        fn.doInitialize();
+        ctxValueFunction.setAttributeValuesFunction(fn);
+        ctxValueFunction.initialize();
+        
+        final ContextDerivedAttributeDefinition defn = new ContextDerivedAttributeDefinition();
+        defn.setAttributeValuesFunction(ctxValueFunction);
+        defn.setId("pDAD");
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);        
+        
+        final IdPAttribute result = defn.resolve(ctx);
+        assertNull(result);
+    }
+    
+    @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 SubjectDerivedAttributeValuesFunction ctxValueFunction = new SubjectDerivedAttributeValuesFunction();
+        ctxValueFunction.setId("pDaD");
+        final IdPAttributePrincipalValuesFunction fn = new IdPAttributePrincipalValuesFunction();
+        fn.setAttributeName("wibble");
+        fn.doInitialize();
+        ctxValueFunction.setAttributeValuesFunction(fn);
+        ctxValueFunction.initialize();
+        
+        final ContextDerivedAttributeDefinition defn = new ContextDerivedAttributeDefinition();
+        defn.setAttributeValuesFunction(ctxValueFunction);
+        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 List<IdPAttributeValue> foo = defn.resolve(ctx).getValues();
+        
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+    
+    @Test public void simpleValueViaSubject() 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 Subject subject = new Subject();
+        subject.getPrincipals().add(new IdPAttributePrincipal(attr));
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+        
+        final SubjectDerivedAttributeValuesFunction ctxValueFunction = new SubjectDerivedAttributeValuesFunction();
+        ctxValueFunction.setId("pDaD");
+        ctxValueFunction.setSubjectLookupStrategy(FunctionSupport.constant(subject));
+        final IdPAttributePrincipalValuesFunction fn = new IdPAttributePrincipalValuesFunction();
+        fn.setAttributeName("wibble");
+        fn.doInitialize();
+        ctxValueFunction.setAttributeValuesFunction(fn);
+        ctxValueFunction.initialize();
+        
+        final ContextDerivedAttributeDefinition defn = new ContextDerivedAttributeDefinition();
+        defn.setAttributeValuesFunction(ctxValueFunction);
+        defn.setId("pDAD");
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);        
+        
+        final List<IdPAttributeValue> foo = defn.resolve(ctx).getValues();
+        
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+    
+    @Test public void simpleValueViaSubjectC14N() 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 Subject subject = new Subject();
+        subject.getPrincipals().add(new IdPAttributePrincipal(attr));
+        subject.getPrincipals().add(new AuthenticationMethodPrincipal(SIMPLE_VALUE + "2"));
+        
+        final SubjectDerivedAttributeValuesFunction ctxValueFunction = new SubjectDerivedAttributeValuesFunction();
+        ctxValueFunction.setId("pDaD");
+        ctxValueFunction.setForCanonicalization(true);
+        final IdPAttributePrincipalValuesFunction fn = new IdPAttributePrincipalValuesFunction();
+        fn.setAttributeName("wibble");
+        fn.doInitialize();
+        ctxValueFunction.setAttributeValuesFunction(fn);
+        ctxValueFunction.initialize();
+        
+        final ContextDerivedAttributeDefinition defn = new ContextDerivedAttributeDefinition();
+        defn.setAttributeValuesFunction(ctxValueFunction);
+        defn.setId("pDAD");
+        defn.initialize();
+
+        final AttributeResolutionContext ctx =
+                TestSources.createResolutionContext(TestSources.PRINCIPAL_ID, TestSources.IDP_ENTITY_ID,
+                        TestSources.SP_ENTITY_ID);        
+        ctx.getParent().getSubcontext(SubjectCanonicalizationContext.class, true).setSubject(subject);
+        
+        final List<IdPAttributeValue> foo = defn.resolve(ctx).getValues();
+        
+        assertEquals(2, foo.size());
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE)));
+        assertTrue(foo.contains(new StringAttributeValue(SIMPLE_VALUE + "2")));
+    }
+        
+    @Test public void empty() throws ComponentInitializationException, ResolutionException {
+        final List<IdPAttributeValue> list = Collections.emptyList();
+        
+        final IdPAttribute attr = new IdPAttribute("wibble");
+        attr.setValues(list);
+
+        final SubjectDerivedAttributeValuesFunction ctxValueFunction = new SubjectDerivedAttributeValuesFunction();
+        ctxValueFunction.setId("pDaD");
+        final IdPAttributePrincipalValuesFunction fn = new IdPAttributePrincipalValuesFunction();
+        fn.setAttributeName("wibble");
+        fn.doInitialize();
+        ctxValueFunction.setAttributeValuesFunction(fn);
+        ctxValueFunction.initialize();
+        
+        final ContextDerivedAttributeDefinition defn = new ContextDerivedAttributeDefinition();
+        defn.setAttributeValuesFunction(ctxValueFunction);
+        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 IdPAttribute result = defn.resolve(ctx);
+        assertNull(result);
+    }
+
+}
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/contextDerived.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/contextDerived.xml
new file mode 100644
index 000000000..5ee934a3e
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/contextDerived.xml
@@ -0,0 +1,5 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    dependencyOnly="1"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+
+    xsi:type="ContextDerivedAttribute" id="PD1" attributeValuesFunctionRef="ctxAttributeValuesFunction"/>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerived.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerived.xml
new file mode 100644
index 000000000..6697b3e38
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerived.xml
@@ -0,0 +1,9 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    dependencyOnly="1"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+
+    xsi:type="SubjectDerivedAttribute" id="PD1" principalAttributeName="Whatever">
+    
+    <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" />
+
+</AttributeDefinition>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedComplex.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedComplex.xml
new file mode 100644
index 000000000..b9d0b9590
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedComplex.xml
@@ -0,0 +1,5 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    dependencyOnly="1"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+
+    xsi:type="SubjectDerivedAttribute" id="PD1" attributeValuesFunctionRef="princialAttributeValuesFunction"/>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedDependency.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedDependency.xml
new file mode 100644
index 000000000..deb66cda2
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedDependency.xml
@@ -0,0 +1,10 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+    dependencyOnly="1"
+    xsi:type="SubjectDerivedAttribute" id="PD1" principalAttributeName="Whatever">
+    
+    <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" />
+    <!--  The following line is here to throw a parsing error -->
+    <InputAttributeDefinition ref="32" />
+
+</AttributeDefinition>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedFail.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedFail.xml
new file mode 100644
index 000000000..714481200
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedFail.xml
@@ -0,0 +1,9 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+
+    dependencyOnly="1"
+    xsi:type="SubjectDerivedAttribute" id="PD1" >
+    
+    <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" />
+
+</AttributeDefinition>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedViaC14N.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedViaC14N.xml
new file mode 100644
index 000000000..38db1e6b6
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedViaC14N.xml
@@ -0,0 +1,9 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    dependencyOnly="1"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+
+    xsi:type="SubjectDerivedAttribute" id="PD1" principalAttributeName="Whatever" forCanonicalization="1">
+    
+    <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" />
+
+</AttributeDefinition>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedWarn.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedWarn.xml
new file mode 100644
index 000000000..f3153693d
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/ad/subjectDerivedWarn.xml
@@ -0,0 +1,8 @@
+ <AttributeDefinition xmlns="urn:mace:shibboleth:2.0:resolver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+    dependencyOnly="1"
+    xsi:type="SubjectDerivedAttribute" id="PD1" principalAttributeName="Whatever" attributeValuesFunctionRef="engineBean">
+    
+    <AttributeEncoder xsi:type="SAML2String" name="urn:oid:0.9.2342.19200300.100.1.1" friendlyName="uid" />
+
+</AttributeDefinition>
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/contextDerivedBeans.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/contextDerivedBeans.xml
new file mode 100644
index 000000000..08f94d5af
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/contextDerivedBeans.xml
@@ -0,0 +1,20 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+       
+    <bean id="princialAttributeValuesFunction" class="net.shibboleth.idp.authn.context.impl.IdPAttributePrincipalValuesFunction" 
+    p:attributeName="BeanWhatever"/>    
+       
+    <bean id="ctxAttributeValuesFunction" class="net.shibboleth.idp.authn.context.impl.SubjectDerivedAttributeValuesFunction" 
+    p:attributeValuesFunction-ref="princialAttributeValuesFunction" p:id="ctxAttributeFunction"/>
+</beans>
+       
\ No newline at end of file
diff --git a/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
new file mode 100644
index 000000000..1032a48a6
--- /dev/null
+++ b/idp-authn-impl/src/test/resources/net/shibboleth/idp/attribute/resolver/spring/customBean.xml
@@ -0,0 +1,50 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+       
+    <util:map id="shibboleth.CustomScriptObject">
+        <entry key="foo" value="bar"/>
+    </util:map>
+        
+    <util:map id="other.CustomScriptObject">
+        <entry key="bar" value="foo"/>
+    </util:map>
+    
+    <bean id="shibboleth.Predicate" destroy-method="" class="com.google.common.base.Predicates" factory-method="alwaysFalse"/>
+    
+    <bean id="shibboleth.PropertySourcesPlaceholderConfigurer" destroy-method=""
+        class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+        p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+    <!-- Necessary for encoder parsing to function, normally part of registry wiring. -->
+    
+    <bean id="SAML2StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2StringAttributeTranscoder" />
+    <bean id="SAML2ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ScopedStringAttributeTranscoder" />
+    <bean id="SAML2ByteTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2ByteAttributeTranscoder" />
+    <bean id="SAML2XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML2XMLObjectAttributeTranscoder" />
+        
+    <bean id="SAML1StringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1StringAttributeTranscoder" />
+    <bean id="SAML1ScopedStringTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ScopedStringAttributeTranscoder" />
+    <bean id="SAML1ByteTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1ByteAttributeTranscoder" />
+    <bean id="SAML1XMLObjectTranscoder"
+        class="net.shibboleth.idp.saml.attribute.transcoding.impl.SAML1XMLObjectAttributeTranscoder" />
+
+</beans>
+
+       
\ No newline at end of file
diff --git a/idp-spring/.classpath b/idp-spring/.classpath
index dc3178b5a..be3679b89 100644
--- a/idp-spring/.classpath
+++ b/idp-spring/.classpath
@@ -6,6 +6,7 @@
 			<attribute name="maven.pomderived" value="true"/>
 		</attributes>
 	</classpathentry>
+	<classpathentry excluding="**" kind="src" path="src/main/resources"/>
 	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
 		<attributes>
 			<attribute name="test" value="true"/>
@@ -21,6 +22,7 @@
 	</classpathentry>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
 		<attributes>
+			<attribute name="module" value="true"/>
 			<attribute name="maven.pomderived" value="true"/>
 		</attributes>
 	</classpathentry>

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


More information about the commits mailing list