[java-shib-profile] branch main updated: JSPROF-1 - Move RelyingParty "layer" into java-shib-profile

Scott Cantor cantor.2 at osu.edu
Wed Feb 15 20:20:49 UTC 2023


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

scantor pushed a commit to branch main
in repository java-shib-profile.

View the commit online:
http://git.shibboleth.net/view/?p=java-shib-profile.git;a=commit;h=0872ac3c68b369e732ffc0707f8ecf4592b278af

The following commit(s) were added to refs/heads/main by this push:
     new 0872ac3  JSPROF-1 - Move RelyingParty "layer" into java-shib-profile
0872ac3 is described below

commit 0872ac3c68b369e732ffc0707f8ecf4592b278af
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Feb 15 15:20:46 2023 -0500

    JSPROF-1 - Move RelyingParty "layer" into java-shib-profile
    
    https://shibboleth.atlassian.net/browse/JSPROF-1
    
    Initial migration of SAML classes.
    Bare-bones SAMLProfileConfiguration base interface.
---
 pom.xml                                            |   2 +-
 shib-profile-bom/pom.xml                           |   2 +-
 .../audit/impl/AttributesAuditExtractor.java       | 100 +++++++
 .../profile/audit/impl/package-info.java           |  21 ++
 .../DefaultRelyingPartyConfigurationResolver.java  |   3 +-
 shib-saml-profile-api/pom.xml                      |  11 +
 .../shibboleth/saml/binding/BindingDescriptor.java |  51 ++++
 .../net/shibboleth/saml/binding/package-info.java  |  21 ++
 .../SAMLArtifactConsumerProfileConfiguration.java  |  48 ++++
 .../profile/config/SAMLProfileConfiguration.java   |  47 ++++
 .../config/logic/SignRequestsPredicate.java        | 108 ++++++++
 .../config/logic/SignResponsesPredicate.java       |  46 ++++
 .../ClientTLSArtifactRequestsPredicate.java        |  46 ++++
 .../messaging/SignArtifactRequestsPredicate.java   |  46 ++++
 .../config/logic/messaging/package-info.java       |  21 ++
 .../saml/profile/config/logic/package-info.java    |  21 ++
 .../saml/profile/config/package-info.java          |  21 ++
 .../logic/MappedEntityAttributesPredicate.java     | 302 +++++++++++++++++++++
 .../saml/profile/context/logic/package-info.java   |  21 ++
 .../SAMLMetadataContextLookupFunction.java         |  51 ++++
 .../profile/context/navigate/package-info.java     |  21 ++
 .../logic/EntityAttributesPredicateTest.java       | 182 +++++++++++++
 .../context/logic/attribute-mapping-metadata.xml   |  63 +++++
 .../context/logic/attribute-registry-service.xml   |  41 +++
 .../profile/context/logic/attribute-registry.xml   |  28 ++
 .../saml/profile/context/logic/rules/subjectid.txt |   3 +
 .../saml/profile/context/logic/rules/zorkmids.txt  |   4 +
 27 files changed, 1328 insertions(+), 3 deletions(-)

diff --git a/pom.xml b/pom.xml
index 0227b39..ce757e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
 
     <modules>
         <module>shib-profile-api</module>
-<!--        <module>shib-saml-profile-api</module>-->
+        <module>shib-saml-profile-api</module>
 
         <module>shib-profile-impl</module>
 <!--        <module>shib-saml-profile-impl</module>-->
diff --git a/shib-profile-bom/pom.xml b/shib-profile-bom/pom.xml
index fe7c7ce..af5e860 100644
--- a/shib-profile-bom/pom.xml
+++ b/shib-profile-bom/pom.xml
@@ -33,12 +33,12 @@
                 <artifactId>shib-profile-impl</artifactId>
                 <version>${project.version}</version>
             </dependency>
-            <!--
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>shib-saml-profile-api</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <!--
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>shib-saml-profile-impl</artifactId>
diff --git a/shib-profile-impl/src/main/java/net/shibboleth/profile/audit/impl/AttributesAuditExtractor.java b/shib-profile-impl/src/main/java/net/shibboleth/profile/audit/impl/AttributesAuditExtractor.java
new file mode 100644
index 0000000..ce7f324
--- /dev/null
+++ b/shib-profile-impl/src/main/java/net/shibboleth/profile/audit/impl/AttributesAuditExtractor.java
@@ -0,0 +1,100 @@
+/*
+ * 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.profile.audit.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.logic.Constraint;
+
+/** {@link Function} that returns the attribute IDs from an {@link AttributeContext}. */
+public class AttributesAuditExtractor implements Function<ProfileRequestContext,Collection<String>> {
+
+    /** Extract the unfiltered attribute list instead of the filtered list. */
+    private boolean useUnfiltered;
+    
+    /** A predicate to control whether attributes should be extracted for logging. */
+    @Nullable private Predicate<ProfileRequestContext> activationCondition;
+    
+    /** Lookup strategy for AttributeContext to read from. */
+    @Nonnull private final Function<ProfileRequestContext,AttributeContext> attributeContextLookupStrategy;
+
+    /** Constructor. */
+    public AttributesAuditExtractor() {
+        // Defaults to ProfileRequestContext -> RelyingPartyContext -> AttributeContext.
+        attributeContextLookupStrategy = new ChildContextLookup<>(AttributeContext.class).compose(
+                new ChildContextLookup<>(RelyingPartyContext.class));
+    }
+    
+    /**
+     * Constructor.
+     *
+     * @param strategy lookup strategy for {@link AttributeContext}
+     */
+    public AttributesAuditExtractor(@Nonnull final Function<ProfileRequestContext,AttributeContext> strategy) {
+        attributeContextLookupStrategy = Constraint.isNotNull(strategy,
+                "AttributeContext lookup strategy cannot be null");
+    }
+    
+    /**
+     * Set whether to extract the list of unfiltered attributes instead of the filtered attributes.
+     * 
+     * @param flag  flag to set
+     */
+    public void setUseUnfiltered(final boolean flag) {
+        useUnfiltered = flag;
+    }
+    
+    /**
+     * Set a condition to evaluate to control whether attributes are extracted for logging.
+     * 
+     * <p>This is used primarily to prevent logging of attributes for profiles in which attributes
+     * may be resolved, but not actually disclosed to a relying party.</p> 
+     * 
+     * @param condition condition to evaluate
+     */
+    public void setActivationCondition(@Nullable final Predicate<ProfileRequestContext> condition) {
+        activationCondition = condition;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public Collection<String> apply(@Nullable final ProfileRequestContext input) {
+        
+        if (activationCondition != null && !activationCondition.test(input)) {
+            return Collections.emptyList();
+        }
+        
+        final AttributeContext attributeCtx = attributeContextLookupStrategy.apply(input);
+        if (attributeCtx != null) {
+            return (useUnfiltered ? attributeCtx.getUnfilteredIdPAttributes()
+                    : attributeCtx.getIdPAttributes()).keySet();
+        }
+        return Collections.emptyList();
+    }
+
+}
\ No newline at end of file
diff --git a/shib-profile-impl/src/main/java/net/shibboleth/profile/audit/impl/package-info.java b/shib-profile-impl/src/main/java/net/shibboleth/profile/audit/impl/package-info.java
new file mode 100644
index 0000000..018eaf7
--- /dev/null
+++ b/shib-profile-impl/src/main/java/net/shibboleth/profile/audit/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Audit implementation classes.
+ */
+package net.shibboleth.profile.audit.impl;
\ No newline at end of file
diff --git a/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java b/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java
index e248705..e0e29da 100644
--- a/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java
+++ b/shib-profile-impl/src/main/java/net/shibboleth/profile/relyingparty/impl/DefaultRelyingPartyConfigurationResolver.java
@@ -240,7 +240,8 @@ public class DefaultRelyingPartyConfigurationResolver extends AbstractIdentifiab
         if (matches.isEmpty()) {
             log.debug("No matching relying party configuration applicable, returning default: {}",
                     getDefaultConfiguration().getId());
-            return CollectionSupport.singleton(getDefaultConfiguration());
+            assert defaultRelyingPartyConfiguration != null;
+            return CollectionSupport.singleton(defaultRelyingPartyConfiguration);
         }
         return matches;
     }
diff --git a/shib-saml-profile-api/pom.xml b/shib-saml-profile-api/pom.xml
index 2699c73..4f397ad 100644
--- a/shib-saml-profile-api/pom.xml
+++ b/shib-saml-profile-api/pom.xml
@@ -28,6 +28,11 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>shib-saml-attribute-impl</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>shib-metadata-api</artifactId>
@@ -56,6 +61,12 @@
         <!-- Runtime Dependencies -->
 
         <!-- Test Dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>shib-attribute-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>shib-metadata-impl</artifactId>
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/binding/BindingDescriptor.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/binding/BindingDescriptor.java
new file mode 100644
index 0000000..3d4eff2
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/binding/BindingDescriptor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.saml.binding;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Subclass that adds awareness of a Spring bean ID for a binding's
+ * {@link org.opensaml.messaging.encoder.MessageEncoder}.
+ */
+public class BindingDescriptor extends org.opensaml.saml.common.binding.BindingDescriptor {
+
+    /** Spring bean ID of message encoder. */
+    @Nullable private String encoderBeanId;
+    
+    /**
+     * Get the Spring bean ID of the binding's {@link org.opensaml.messaging.encoder.MessageEncoder}.
+     * 
+     * @return bean ID of message encoder
+     */
+    @Nullable public String getEncoderBeanId() {
+        return encoderBeanId;
+    }
+
+    /**
+     * Set the Spring bean ID of the binding's {@link org.opensaml.messaging.encoder.MessageEncoder}.
+     * 
+     * @param id bean ID of message encoder
+     */
+    public void setEncoderBeanId(@Nullable final String id) {
+        encoderBeanId = StringSupport.trimOrNull(id);
+    }
+    
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/binding/package-info.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/binding/package-info.java
new file mode 100644
index 0000000..1ec56b0
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/binding/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to SAML bindings.
+ */
+package net.shibboleth.saml.binding;
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/SAMLArtifactConsumerProfileConfiguration.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/SAMLArtifactConsumerProfileConfiguration.java
new file mode 100644
index 0000000..6c8c63c
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/SAMLArtifactConsumerProfileConfiguration.java
@@ -0,0 +1,48 @@
+/*
+ * 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.saml.profile.config;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+
+/**
+ * Common interface for SAML profile configurations involving artifact consumption, for example artifact
+ * resolution requests. 
+ */
+public interface SAMLArtifactConsumerProfileConfiguration extends SAMLProfileConfiguration {
+    
+    /**
+     * Get whether artifact resolution requests should be signed.
+     * 
+     * @param messageContext current message context
+     * 
+     * @return whether artifact resolution requests should be signed
+     */
+    boolean isSignArtifactRequests(@Nullable final MessageContext messageContext); 
+
+    /**
+     * Get whether artifact resolution requests should use client TLS.
+     * 
+     * @param messageContext current message context
+     * 
+     * @return whether artifact resolution requests should use client TLS
+     */
+    boolean isClientTLSArtifactRequests(@Nullable final MessageContext messageContext); 
+
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/SAMLProfileConfiguration.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/SAMLProfileConfiguration.java
new file mode 100644
index 0000000..ca7ad64
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/SAMLProfileConfiguration.java
@@ -0,0 +1,47 @@
+/*
+ * 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.saml.profile.config;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+
+/** Common interface for SAML profile configurations. */
+public interface SAMLProfileConfiguration extends ProfileConfiguration {
+
+    /**
+     * Get the predicate used to determine if generated requests should be signed.
+     * 
+     * @param profileRequestContext current profile request context
+     * 
+     * @return predicate used to determine if generated requests should be signed
+     */
+    boolean isSignRequests(@Nullable final ProfileRequestContext profileRequestContext);
+
+    /**
+     * Get the predicate used to determine if generated responses should be signed.
+     * 
+     * @param profileRequestContext current profile request context
+     * 
+     * @return predicate used to determine if generated responses should be signed
+     */
+    boolean isSignResponses(@Nullable final ProfileRequestContext profileRequestContext);
+    
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/SignRequestsPredicate.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/SignRequestsPredicate.java
new file mode 100644
index 0000000..0b09855
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/SignRequestsPredicate.java
@@ -0,0 +1,108 @@
+/*
+ * 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.saml.profile.config.logic;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.logic.AbstractRelyingPartyPredicate;
+import net.shibboleth.saml.profile.context.navigate.SAMLMetadataContextLookupFunction;
+import net.shibboleth.saml.profile.config.SAMLProfileConfiguration;
+import net.shibboleth.shared.logic.Constraint;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
+import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.RoleDescriptor;
+
+/**
+ * A predicate implementation that forwards to 
+ * {@link SAMLProfileConfiguration#isSignRequests(ProfileRequestContext)}
+ * or follows {@link IDPSSODescriptor#getWantAuthnRequestsSigned()} if so configured.
+ */
+public class SignRequestsPredicate extends AbstractRelyingPartyPredicate {
+    
+    /** Whether to override the result based on the WantAuthnRequestsSigned flag in SAML metadata. */
+    private boolean honorMetadata;
+    
+    /** Lookup strategy for {@link SAMLMetadataContext}. */
+    private Function<ProfileRequestContext,SAMLMetadataContext> metadataContextLookupStrategy;
+    
+    /** Constructor. */
+    public SignRequestsPredicate() {
+        metadataContextLookupStrategy = new SAMLMetadataContextLookupFunction();
+    }
+    
+    /**
+     * Set whether to override the result based on the WantAuthnRequestsSigned flag in SAML metadata.
+     * 
+     * <p>Defaults to false.</p>
+     * 
+     * @param flag flag to set
+     * 
+     * @since 4.0.0
+     */
+    public void setHonorMetadata(final boolean flag) {
+        honorMetadata = flag;
+    }
+
+    /**
+     * Set lookup strategy for {@link SAMLMetadataContext}.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 4.0.0
+     */
+    public void setMetadataContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,SAMLMetadataContext> strategy) {
+        metadataContextLookupStrategy = Constraint.isNotNull(strategy,
+                "SAMLMetadataContext lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    public boolean test(@Nullable final ProfileRequestContext input) {
+
+        if (honorMetadata) {
+            final SAMLMetadataContext metadataCtx = metadataContextLookupStrategy.apply(input);
+            if (metadataCtx != null) {
+                final RoleDescriptor role = metadataCtx.getRoleDescriptor();
+                if (role instanceof IDPSSODescriptor) {
+                    final Boolean flag = ((IDPSSODescriptor) role).getWantAuthnRequestsSigned();
+                    if (flag != null && flag.booleanValue()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        
+        final RelyingPartyContext rpc = getRelyingPartyContext(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc != null && pc instanceof SAMLProfileConfiguration) {
+                return ((SAMLProfileConfiguration) pc).isSignRequests(input);
+            }
+        }
+        
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/SignResponsesPredicate.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/SignResponsesPredicate.java
new file mode 100644
index 0000000..01b6fc4
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/SignResponsesPredicate.java
@@ -0,0 +1,46 @@
+/*
+ * 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.saml.profile.config.logic;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.logic.AbstractRelyingPartyPredicate;
+import net.shibboleth.saml.profile.config.SAMLProfileConfiguration;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+/** A predicate implementation that forwards
+ * to {@link SAMLProfileConfiguration#isSignResponses(ProfileRequestContext)}. */
+public class SignResponsesPredicate extends AbstractRelyingPartyPredicate {
+    
+    /** {@inheritDoc} */
+    public boolean test(@Nullable final ProfileRequestContext input) {
+        final RelyingPartyContext rpc = getRelyingPartyContext(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc instanceof SAMLProfileConfiguration) {
+                return ((SAMLProfileConfiguration) pc).isSignResponses(input);
+            }
+        }
+        
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/ClientTLSArtifactRequestsPredicate.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/ClientTLSArtifactRequestsPredicate.java
new file mode 100644
index 0000000..0d030cb
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/ClientTLSArtifactRequestsPredicate.java
@@ -0,0 +1,46 @@
+/*
+ * 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.saml.profile.config.logic.messaging;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.logic.messaging.AbstractRelyingPartyPredicate;
+import net.shibboleth.saml.profile.config.SAMLArtifactConsumerProfileConfiguration;
+
+/** A predicate implementation that forwards to 
+ * {@link SAMLArtifactConsumerProfileConfiguration#isClientTLSArtifactRequests(MessageContext)}. */
+public class ClientTLSArtifactRequestsPredicate extends AbstractRelyingPartyPredicate {
+    
+    /** {@inheritDoc} */
+    public boolean test(@Nullable final MessageContext input) {
+        final RelyingPartyContext rpc = getRelyingPartyContext(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc != null && pc instanceof SAMLArtifactConsumerProfileConfiguration) {
+                return ((SAMLArtifactConsumerProfileConfiguration) pc).isClientTLSArtifactRequests(input);
+            }
+        }
+        
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/SignArtifactRequestsPredicate.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/SignArtifactRequestsPredicate.java
new file mode 100644
index 0000000..4c2e230
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/SignArtifactRequestsPredicate.java
@@ -0,0 +1,46 @@
+/*
+ * 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.saml.profile.config.logic.messaging;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.MessageContext;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.logic.messaging.AbstractRelyingPartyPredicate;
+import net.shibboleth.saml.profile.config.SAMLArtifactConsumerProfileConfiguration;
+
+/** A predicate implementation that forwards to 
+ * {@link SAMLArtifactConsumerProfileConfiguration#isSignArtifactRequests(MessageContext)}. */
+public class SignArtifactRequestsPredicate extends AbstractRelyingPartyPredicate {
+    
+    /** {@inheritDoc} */
+    public boolean test(@Nullable final MessageContext input) {
+        final RelyingPartyContext rpc = getRelyingPartyContext(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc != null && pc instanceof SAMLArtifactConsumerProfileConfiguration) {
+                return ((SAMLArtifactConsumerProfileConfiguration) pc).isSignArtifactRequests(input);
+            }
+        }
+        
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/package-info.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/package-info.java
new file mode 100644
index 0000000..4e058f5
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/messaging/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Predicates based on message contexts and SAML profile configuration.
+ */
+package net.shibboleth.saml.profile.config.logic.messaging;
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/package-info.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/package-info.java
new file mode 100644
index 0000000..b75813e
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/logic/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Predicates based on SAML profile configuration.
+ */
+package net.shibboleth.saml.profile.config.logic;
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/package-info.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/package-info.java
new file mode 100644
index 0000000..7262525
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/config/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Generic SAML profile configuration.
+ */
+package net.shibboleth.saml.profile.config;
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/logic/MappedEntityAttributesPredicate.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/logic/MappedEntityAttributesPredicate.java
new file mode 100644
index 0000000..7576340
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/logic/MappedEntityAttributesPredicate.java
@@ -0,0 +1,302 @@
+/*
+ * 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.saml.profile.context.logic;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate;
+import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate.Candidate;
+import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+import net.shibboleth.idp.attribute.AttributesMapContainer;
+import net.shibboleth.idp.attribute.EmptyAttributeValue;
+import net.shibboleth.idp.attribute.EmptyAttributeValue.EmptyType;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+
+import java.util.function.Predicate;
+
+/**
+ * Extended version of EntityAttributes-driven predicate that uses an optimization to check
+ * for mapped attributes in an {@link AttributesMapContainer} structure.
+ */
+public class MappedEntityAttributesPredicate extends EntityAttributesPredicate {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(MappedEntityAttributesPredicate.class);
+    
+    /** Delimiter to build string form of scoped values. */
+    @Nonnull @NotEmpty private String scopeDelimiter = "@";
+    
+    /**
+     * Constructor.
+     * 
+     * @param candidates the {@link Candidate} criteria to check for
+     */
+    public MappedEntityAttributesPredicate(
+            @Nonnull @NonnullElements @ParameterName(name="candidates") final Collection<Candidate> candidates) {
+        super(candidates);
+
+        Constraint.isTrue(Iterables.all(candidates, c -> c.getNameFormat() == null),
+                "Use of nameFormat property is impermissible with MappedEntityAttributesPredicate");
+    }
+
+    /**
+     * Constructor.
+     * 
+     * @param candidates the {@link Candidate} criteria to check for
+     * @param trim true iff the values found in the metadata should be trimmed before comparison
+     */
+    public MappedEntityAttributesPredicate(
+            @Nonnull @NonnullElements @ParameterName(name="candidates") final Collection<Candidate> candidates,
+            @ParameterName(name="trim") final boolean trim) {
+        super(candidates, trim);
+        
+        Constraint.isTrue(Iterables.all(candidates, c -> c.getNameFormat() == null),
+                "Use of nameFormat property is impermissible with MappedEntityAttributesPredicate");
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param candidates the {@link Candidate} criteria to check for
+     * @param trim true iff the values found in the metadata should be trimmed before comparison
+     * @param all true iff all the criteria must match to be a successful test
+     */
+    public MappedEntityAttributesPredicate(
+            @Nonnull @NonnullElements @ParameterName(name="candidates") final Collection<Candidate> candidates,
+            @ParameterName(name="trim") final boolean trim,
+            @ParameterName(name="all") final boolean all) {
+        super(candidates, trim, all);
+        
+        Constraint.isTrue(Iterables.all(candidates, c -> c.getNameFormat() == null),
+                "Use of nameFormat property is impermissible with MappedEntityAttributesPredicate");
+    }
+    
+    /**
+     * Set delimiter for constructing scoped values for comparison.
+     * 
+     * <p>Defaults to '@'.</p>
+     * 
+     * @param delimiter delimiter to use
+     */
+    public void setScopeDelimiter(@Nonnull @NotEmpty final String delimiter) {
+        scopeDelimiter = Constraint.isNotNull(StringSupport.trimOrNull(delimiter),
+                "Scope delimiter cannot be null or empty");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public boolean test(@Nullable final EntityDescriptor input) {
+        
+        if (getCandidates().isEmpty()) {
+            return true;
+        } else if (input == null) {
+            return false;
+        }
+        
+        final Collection<Candidate> candidates = new ArrayList<>(getCandidates());
+
+        if (doTest(input, input.getEntityID(), candidates)) {
+            
+            // At least one match. Check if sufficient.
+            if (!getMatchAll() || candidates.isEmpty()) {
+                return true;
+            }
+        }
+        
+        XMLObject parent = input.getParent();
+        while (parent instanceof EntitiesDescriptor) {
+            if (doTest(parent, ((EntitiesDescriptor) parent).getName(), candidates)) {
+                
+                // At least one match. Check if sufficient.
+                if (!getMatchAll() || candidates.isEmpty()) {
+                    return true;
+                }
+            }
+            parent = parent.getParent();
+        }
+        
+        return false;
+    }
+    
+    /**
+     * Evaluate the input object's attached object metadata against the supplied candidates.
+     * 
+     * <p>Any candidates that match will be removed from the input collection.</p>
+     * 
+     * @param input input object
+     * @param name label for logging
+     * @param candidates candidates to check
+     * 
+     * @return true iff the attached object metadata matched at least one input candidate
+     */
+    private boolean doTest(@Nonnull final XMLObject input, @Nullable final String name,
+            @Nonnull @NonnullElements final Collection<Candidate> candidates) {
+        final List<AttributesMapContainer> containerList =
+                input.getObjectMetadata().get(AttributesMapContainer.class);
+        if (null == containerList || containerList.isEmpty() || containerList.get(0).get() == null ||
+                containerList.get(0).get().isEmpty()) {
+            log.trace("No mapped Entity Attributes for {}", name);
+            return false;
+        }
+        
+        final Multimap<String,? extends IdPAttribute> entityAttributes = containerList.get(0).get();
+        
+        log.trace("Checking for match against {} Entity Attributes for {}", entityAttributes.size(),
+                name);
+        
+        // Remove each candidate that matches. Tag values are OR'd for matching purposes.
+        // Return true iff at least one candidate matches.
+        return candidates.removeIf(new EntityAttributesMatcher(entityAttributes));
+    }
+
+    /**
+     * Determines whether an {@link Candidate} criterion is satisfied by the mapped
+     * attributes in an entity's metadata.
+     */
+    private class EntityAttributesMatcher implements Predicate<Candidate> {
+        
+        /** Population to evaluate for a match. */
+        @Nonnull private final Multimap<String,? extends IdPAttribute> attributes;
+        
+        /**
+         * Constructor.
+         *
+         * @param attrs population to evaluate for a match
+         */
+        public EntityAttributesMatcher(@Nonnull @NonnullElements final Multimap<String,? extends IdPAttribute> attrs) {
+            attributes = Constraint.isNotNull(attrs, "Extension attributes cannot be null");
+        }
+
+// Checkstyle: CyclomaticComplexity OFF
+        /** {@inheritDoc} */
+        public boolean test(@Nullable final Candidate input) {
+            
+            if (input == null || input.getNameFormat() != null) {
+                return false;
+            }
+            
+            final List<String> tagvals = input.getValues();
+            final List<Pattern> tagexps = input.getRegexps();
+
+            // Track whether we've found every match we need (possibly with arrays of 0 size).
+            final boolean[] valflags = new boolean[tagvals.size()];
+            final boolean[] expflags = new boolean[tagexps.size()];
+
+            // Check each attribute/tag in the populated set.
+            for (final IdPAttribute a : attributes.get(input.getName())) {
+
+                // Check each tag value's simple content for a value match.
+                for (int tagindex = 0; tagindex < tagvals.size(); ++tagindex) {
+                    final String tagvalstr = tagvals.get(tagindex);
+
+                    for (final IdPAttributeValue cval : a.getValues()) {
+                        assert cval != null;
+                        final String cvalstr = attributeValueToString(cval);
+                        if (tagvalstr != null && cvalstr != null) {
+                            if (tagvalstr.equals(cvalstr)) {
+                                log.trace("Matched mapped Entity Attribute ({}) value {}", a.getId(), tagvalstr);
+                                valflags[tagindex] = true;
+                                break;
+                            } else if (getTrimTags()) {
+                                if (tagvalstr.equals(cvalstr.trim())) {
+                                    log.trace("Matched mapped Entity Attribute ({}) value {}", a.getId(), tagvalstr);
+                                    valflags[tagindex] = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // Check each tag regular expression for a match.
+                for (int tagindex = 0; tagindex < tagexps.size(); ++tagindex) {
+
+                    for (final IdPAttributeValue cval : a.getValues()) {
+                        assert cval != null;
+                        final String cvalstr = attributeValueToString(cval);
+                        if (tagexps.get(tagindex) != null && cvalstr != null) {
+                            if (tagexps.get(tagindex).matcher(cvalstr).matches()) {
+                                log.trace("Matched mapped Entity Attribute ({}) value {}", a.getId(), cvalstr);
+                                expflags[tagindex] = true;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            for (final boolean flag : valflags) {
+                if (!flag) {
+                    return false;
+                }
+            }
+
+            for (final boolean flag : expflags) {
+                if (!flag) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+// Checkstyle: CyclomaticComplexity ON
+     
+        /**
+         * Convert an IdPAttributeValue to a String if the type is recognized.
+         * 
+         * @param value object to convert
+         * @return the converted value, or null
+         */
+        @Nullable private String attributeValueToString(@Nonnull final IdPAttributeValue value) {
+            
+            if (value instanceof ScopedStringAttributeValue) {
+                return ((ScopedStringAttributeValue) value).getValue() + scopeDelimiter +
+                        ((ScopedStringAttributeValue) value).getScope();
+            } else if (value instanceof StringAttributeValue) {
+                return ((StringAttributeValue) value).getValue();
+            } else if (value instanceof EmptyAttributeValue) {
+                final EmptyType empty = ((EmptyAttributeValue) value).getValue();
+                return EmptyType.ZERO_LENGTH_VALUE.equals(empty) ? "" : null;
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/logic/package-info.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/logic/package-info.java
new file mode 100644
index 0000000..c0e1ba0
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/logic/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Predicates operating against SAML contexts.
+ */
+package net.shibboleth.saml.profile.context.logic;
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/navigate/SAMLMetadataContextLookupFunction.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/navigate/SAMLMetadataContextLookupFunction.java
new file mode 100644
index 0000000..29a0607
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/navigate/SAMLMetadataContextLookupFunction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.saml.profile.context.navigate;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+
+import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
+
+/**
+ * A function to access a {@link SAMLMetadataContext} underlying a {@link RelyingPartyContext} located via a
+ * lookup function, by default a child of the profile request context.
+ */
+public class SAMLMetadataContextLookupFunction extends AbstractRelyingPartyLookupFunction<SAMLMetadataContext> {
+
+    /** {@inheritDoc} */
+    @Nullable public SAMLMetadataContext apply(@Nullable final ProfileRequestContext input) {
+        final RelyingPartyContext rpCtx = getRelyingPartyContext(input);
+        if (rpCtx != null) {
+            final BaseContext peer = rpCtx.getRelyingPartyIdContextTree();
+            if (peer != null) {
+                if (peer instanceof SAMLMetadataContext) {
+                    return (SAMLMetadataContext) peer;
+                }
+                return peer.getSubcontext(SAMLMetadataContext.class);    
+            }
+        }
+        
+        return null;
+    }
+    
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/navigate/package-info.java b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/navigate/package-info.java
new file mode 100644
index 0000000..dd1d585
--- /dev/null
+++ b/shib-saml-profile-api/src/main/java/net/shibboleth/saml/profile/context/navigate/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Context lookup functions related to SAML contexts.
+ */
+package net.shibboleth.saml.profile.context.navigate;
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/test/java/net/shibboleth/saml/profile/context/logic/EntityAttributesPredicateTest.java b/shib-saml-profile-api/src/test/java/net/shibboleth/saml/profile/context/logic/EntityAttributesPredicateTest.java
new file mode 100644
index 0000000..f44019b
--- /dev/null
+++ b/shib-saml-profile-api/src/test/java/net/shibboleth/saml/profile/context/logic/EntityAttributesPredicateTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.saml.profile.context.logic;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.criterion.EntityIdCriterion;
+import org.opensaml.core.testing.XMLObjectBaseTestCase;
+import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate;
+import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate.Candidate;
+import org.opensaml.saml.metadata.resolver.MetadataResolver;
+import org.opensaml.saml.metadata.resolver.filter.MetadataNodeProcessor;
+import org.opensaml.saml.metadata.resolver.filter.impl.NodeProcessingMetadataFilter;
+import org.opensaml.saml.metadata.resolver.impl.DOMMetadataResolver;
+import org.opensaml.saml.saml2.core.Attribute;
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.springframework.context.support.ConversionServiceFactoryBean;
+import org.springframework.context.support.GenericApplicationContext;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+
+import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
+import net.shibboleth.idp.saml.attribute.impl.AttributeMappingNodeProcessor;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.spring.config.StringToDurationConverter;
+import net.shibboleth.shared.spring.custom.SchemaTypeAwareXMLBeanDefinitionReader;
+
+/**
+ * Unit test for {@link EntityAttributesPredicate} and {@link MappedEntityAttributesPredicate}.
+ */
+ at SuppressWarnings("javadoc")
+public class EntityAttributesPredicateTest extends XMLObjectBaseTestCase {
+    
+    @Nonnull @NotEmpty private String fooEntityID = "http://foo.example.org/shibboleth";
+    
+    @Nonnull @NotEmpty private String barEntityID = "http://bar.example.org/shibboleth";
+    
+    @Nonnull @NotEmpty private String bazEntityID = "http://baz.example.org/shibboleth";
+    
+    @Nullable private GenericApplicationContext pendingTeardownContext;
+    
+    @Nullable private MetadataResolver resolver;
+    
+    @BeforeClass
+    protected void setUp() throws Exception {
+        final String mdFileName = "/net/shibboleth/saml/profile/context/logic/attribute-mapping-metadata.xml";
+        
+        final Document mdDoc = parserPool.parse(getClass().getResourceAsStream(mdFileName));
+        final DOMMetadataResolver mdProvider = new DOMMetadataResolver(mdDoc.getDocumentElement());
+        
+        final ArrayList<MetadataNodeProcessor> processors = new ArrayList<>();
+        processors.add(new AttributeMappingNodeProcessor(getService()));
+        
+        final NodeProcessingMetadataFilter nodeFilter =  new NodeProcessingMetadataFilter();
+        nodeFilter.setNodeProcessors(processors);
+        nodeFilter.initialize();
+        
+        mdProvider.setMetadataFilter(nodeFilter);
+        mdProvider.setId("Test");
+        mdProvider.initialize();
+
+        resolver = mdProvider;
+    }
+    
+    @AfterClass public void tearDownTestContext() {
+        if (null != pendingTeardownContext) {
+            pendingTeardownContext.close();
+            pendingTeardownContext = null;
+        }
+    }
+    
+    private void setTestContext(GenericApplicationContext context) {
+        tearDownTestContext();
+        pendingTeardownContext = context;
+    }
+    
+    private EntityDescriptor getEntity(@Nonnull final String entityID) throws ResolverException {
+        assert resolver != null;
+        return resolver.resolveSingle(new CriteriaSet(new EntityIdCriterion(entityID)));
+    }
+    
+    private ReloadableService<AttributeTranscoderRegistry> getService() {
+        final GenericApplicationContext context = new GenericApplicationContext();
+        setTestContext(context);
+        context.setDisplayName("ApplicationContext: ");
+
+        final ConversionServiceFactoryBean service = new ConversionServiceFactoryBean();
+        service.setConverters(new HashSet<>(CollectionSupport.listOf(new StringToDurationConverter())));
+        service.afterPropertiesSet();
+
+        context.getBeanFactory().setConversionService(service.getObject());
+        
+        final SchemaTypeAwareXMLBeanDefinitionReader beanDefinitionReader =
+                new SchemaTypeAwareXMLBeanDefinitionReader(context);
+
+        beanDefinitionReader.loadBeanDefinitions("/net/shibboleth/saml/profile/context/logic/attribute-registry-service.xml");
+        context.refresh();
+
+        return context.getBean(ReloadableService.class);
+    }
+
+    @Test
+    public void testSimpleMatch() throws ResolverException {
+        
+        final Candidate tag = new Candidate("zorkmids", Attribute.BASIC);
+        tag.setValues(CollectionSupport.singletonList("10"));
+        
+        final EntityAttributesPredicate predicate = new EntityAttributesPredicate(CollectionSupport.singletonList(tag));
+        Assert.assertFalse(predicate.test(getEntity(fooEntityID)));
+        Assert.assertTrue(predicate.test(getEntity(barEntityID)));
+        Assert.assertFalse(predicate.test(getEntity(bazEntityID)));
+    }    
+    
+    @Test
+    public void testMultiLevelMatch() throws ResolverException {
+        final Candidate tag1 = new Candidate("http://macedir.org/entity-category", Attribute.URI_REFERENCE);
+        tag1.setValues(CollectionSupport.singletonList("http://refeds.org/category/research-and-scholarship"));
+
+        final Candidate tag2 = new Candidate("urn:oasis:names:tc:SAML:profiles:subject-id:req", Attribute.URI_REFERENCE);
+        tag2.setValues(CollectionSupport.singletonList("none"));
+
+        final EntityAttributesPredicate predicate =
+                new EntityAttributesPredicate(CollectionSupport.listOf(tag1, tag2), false, true);
+        Assert.assertTrue(predicate.test(getEntity(fooEntityID)));
+        Assert.assertFalse(predicate.test(getEntity(barEntityID)));
+        Assert.assertFalse(predicate.test(getEntity(bazEntityID)));
+    }
+    
+    @Test
+    public void testSimpleMatchMapped() throws ResolverException {
+        
+        final Candidate tag = new Candidate("zorkmids");
+        tag.setValues(CollectionSupport.singletonList("10"));
+        
+        final MappedEntityAttributesPredicate predicate = new MappedEntityAttributesPredicate(CollectionSupport.singletonList(tag));
+        Assert.assertFalse(predicate.test(getEntity(fooEntityID)));
+        Assert.assertTrue(predicate.test(getEntity(barEntityID)));
+        Assert.assertFalse(predicate.test(getEntity(bazEntityID)));
+    }
+
+    @Test
+    public void testMultiLevelMappedMatch() throws ResolverException {
+        final Candidate tag1 = new Candidate("http://macedir.org/entity-category");
+        tag1.setValues(CollectionSupport.singletonList("http://refeds.org/category/research-and-scholarship"));
+
+        final Candidate tag2 = new Candidate("subject-id-req");
+        tag2.setValues(CollectionSupport.singletonList("none"));
+
+        final MappedEntityAttributesPredicate predicate =
+                new MappedEntityAttributesPredicate(CollectionSupport.listOf(tag1, tag2), false, true);
+        Assert.assertTrue(predicate.test(getEntity(fooEntityID)));
+        Assert.assertFalse(predicate.test(getEntity(barEntityID)));
+        Assert.assertFalse(predicate.test(getEntity(bazEntityID)));
+    }
+
+}
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-mapping-metadata.xml b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-mapping-metadata.xml
new file mode 100644
index 0000000..a9850f5
--- /dev/null
+++ b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-mapping-metadata.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntitiesDescriptor Name="example.org"
+    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+    xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute">
+    
+    <Extensions>
+        <mdattr:EntityAttributes>
+            <saml:Attribute Name="http://macedir.org/entity-category"
+                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                <saml:AttributeValue>http://refeds.org/category/research-and-scholarship</saml:AttributeValue>
+            </saml:Attribute>
+        </mdattr:EntityAttributes>
+    </Extensions>
+   
+    <EntityDescriptor entityID="http://foo.example.org/shibboleth">
+      
+      <Extensions>
+        <mdattr:EntityAttributes>
+            <saml:Attribute Name="urn:oasis:names:tc:SAML:profiles:subject-id:req"
+                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                <saml:AttributeValue>none</saml:AttributeValue>
+            </saml:Attribute>
+        </mdattr:EntityAttributes>
+      </Extensions>
+      
+      <IDPSSODescriptor protocolSupportEnumeration="foo">
+         <SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://foo.example.org/shibboleth/SSO_BAR"/>
+      </IDPSSODescriptor>
+      
+   </EntityDescriptor>
+   
+
+   <EntityDescriptor entityID="http://bar.example.org/shibboleth">
+
+      <Extensions>
+        <mdattr:EntityAttributes>
+            <saml:Attribute Name="zorkmids"
+                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue>10</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute Name="babelfish"
+                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue>10</saml:AttributeValue>
+            </saml:Attribute>
+        </mdattr:EntityAttributes>
+      </Extensions>
+      
+      <SPSSODescriptor protocolSupportEnumeration="foo">
+         <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" Location="https://bar.example.org/Shibboleth.sso/POST" index="0"/>
+      </SPSSODescriptor>
+      
+   </EntityDescriptor>
+
+   <EntityDescriptor entityID="http://baz.example.org/shibboleth">
+
+      <SPSSODescriptor protocolSupportEnumeration="foo">
+         <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" Location="https://baz.example.org/Shibboleth.sso/POST" index="0"/>
+      </SPSSODescriptor>
+      
+   </EntityDescriptor>
+
+</EntitiesDescriptor>
diff --git a/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-registry-service.xml b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-registry-service.xml
new file mode 100644
index 0000000..6046385
--- /dev/null
+++ b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-registry-service.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+        xmlns:context="http://www.springframework.org/schema/context"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xmlns:util="http://www.springframework.org/schema/util"
+        xmlns:p="http://www.springframework.org/schema/p"
+        xmlns:c="http://www.springframework.org/schema/c"
+        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">
+
+    <!-- Needed to allow for auto-wiring of descriptors and other componnents. -->
+    <context:annotation-config />
+        
+    <bean id="SAML2NamingFunction" destroy-method=""
+            class="net.shibboleth.idp.attribute.transcoding.BasicNamingFunction"
+            c:claz="org.opensaml.saml.saml2.core.Attribute">
+        <constructor-arg name="function">
+            <bean destroy-method=""
+                class="net.shibboleth.idp.saml.attribute.transcoding.AbstractSAML2AttributeTranscoder.NamingFunction" />
+        </constructor-arg>
+    </bean>
+    
+    <bean id="RegistryServiceStrategy" class="net.shibboleth.idp.attribute.transcoding.impl.AttributeRegistryServiceStrategy"
+        p:id="shibboleth.AttributeRegistryService" />
+
+    <bean id="shibboleth.AttributeRegistryService" class="net.shibboleth.shared.spring.service.ReloadableSpringService"
+            c:claz="net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry"
+            c:strategy-ref="RegistryServiceStrategy"
+            p:failFast="true"
+            p:reloadCheckDelay="0">
+        <property name="serviceConfigurations">
+            <util:list>
+                <value>net/shibboleth/saml/profile/context/logic/attribute-registry.xml</value>
+            </util:list>
+        </property>
+    </bean>
+
+</beans>
\ No newline at end of file
diff --git a/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-registry.xml b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-registry.xml
new file mode 100644
index 0000000..137c7f1
--- /dev/null
+++ b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/attribute-registry.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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="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="shibboleth.TranscodingRuleLoader" destroy-method=""
+        class="net.shibboleth.idp.attribute.transcoding.impl.TranscodingRuleLoader"
+        c:dir="net/shibboleth/saml/profile/context/logic/rules" />
+    
+</beans>
diff --git a/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/rules/subjectid.txt b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/rules/subjectid.txt
new file mode 100644
index 0000000..17d1ceb
--- /dev/null
+++ b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/rules/subjectid.txt
@@ -0,0 +1,3 @@
+id = subject-id-req
+transcoder = SAML2StringTranscoder
+saml2.name = urn:oasis:names:tc:SAML:profiles:subject-id:req
diff --git a/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/rules/zorkmids.txt b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/rules/zorkmids.txt
new file mode 100644
index 0000000..1b25004
--- /dev/null
+++ b/shib-saml-profile-api/src/test/resources/net/shibboleth/saml/profile/context/logic/rules/zorkmids.txt
@@ -0,0 +1,4 @@
+id = zorkmids
+transcoder = SAML2StringTranscoder
+saml2.name = zorkmids
+saml2.nameFormat = urn:oasis:names:tc:SAML:2.0:attrname-format:basic

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


More information about the commits mailing list