[java-identity-provider] branch master updated: IDP-1229 - Add Scoping/RequesterID chains into various IdP features

Scott Cantor cantor.2 at osu.edu
Wed Nov 1 17:49:08 EDT 2017


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

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

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

The following commit(s) were added to refs/heads/master by this push:
       new  266a820   IDP-1229 - Add Scoping/RequesterID chains into various IdP features
266a820 is described below

commit 266a820401d762b5a20d0b8bd52324abac15c0cb
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Nov 1 17:49:05 2017 -0400

    IDP-1229 - Add Scoping/RequesterID chains into various IdP features
    
    https://issues.shibboleth.net/jira/browse/IDP-1229
    
    Attribute filtering support
---
 .../filter/context/AttributeFilterContext.java     |  67 ++++++++++++--
 .../impl/ProxiedRequesterPolicyRule.java           |  68 ++++++++++++++
 .../impl/ProxiedRequesterRegexpPolicyRule.java     |  64 +++++++++++++
 .../impl/ProxiedRequesterPolicyRuleTest.java       | 102 +++++++++++++++++++++
 .../impl/ProxiedRequesterRegexpPolicyRuleTest.java |  81 ++++++++++++++++
 .../impl/AttributeFilterNamespaceHandler.java      |   7 ++
 .../impl/ProxiedRequesterRegexRuleParser.java      |  44 +++++++++
 .../impl/ProxiedRequesterRuleParser.java           |  44 +++++++++
 .../ProxiedRequesterRegexRuleParserTest.java       |  36 ++++++++
 .../policy/ProxiedRequesterRuleParserTest.java     |  95 +++++++++++++++++++
 .../filter/matcher/afp/proxiedRequester.xml        |   4 +
 .../policyrule/afp/proxiedRegexRequester.xml       |   4 +
 .../filter/policyrule/afp/proxiedRequester.xml     |   4 +
 .../idp/profile/impl/FilterAttributes.java         |  58 +++++++++---
 .../src/main/resources/schema/shibboleth-afp.xsd   |  22 +++++
 15 files changed, 680 insertions(+), 20 deletions(-)

diff --git a/idp-attribute-filter-api/src/main/java/net/shibboleth/idp/attribute/filter/context/AttributeFilterContext.java b/idp-attribute-filter-api/src/main/java/net/shibboleth/idp/attribute/filter/context/AttributeFilterContext.java
index 638ee3e..8b1447d 100644
--- a/idp-attribute-filter-api/src/main/java/net/shibboleth/idp/attribute/filter/context/AttributeFilterContext.java
+++ b/idp-attribute-filter-api/src/main/java/net/shibboleth/idp/attribute/filter/context/AttributeFilterContext.java
@@ -34,6 +34,7 @@ import net.shibboleth.utilities.java.support.collection.CollectionSupport;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 
 import org.opensaml.messaging.context.BaseContext;
+import org.opensaml.profile.context.ProxiedRequesterContext;
 import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
 import org.opensaml.saml.metadata.resolver.MetadataResolver;
 
@@ -68,10 +69,17 @@ public final class AttributeFilterContext extends BaseContext {
     /** Cache of the metadata context. */
     @Nullable private SAMLMetadataContext requesterMetadataContext;
 
+    /** Cache of the proxied requester context. */
+    @Nullable private ProxiedRequesterContext proxiedRequesterContext;
+
     /** Lookup strategy used to locate the SP's metadata context. */
-    @NonnullAfterInit
+    @Nullable
     private Function<AttributeFilterContext,SAMLMetadataContext> requesterMetadataContextLookupStrategy;
 
+    /** Lookup strategy used to locate a {@link ProxiedRequesterContext}. */
+    @Nullable
+    private Function<AttributeFilterContext,ProxiedRequesterContext> proxiedRequesterContextLookupStrategy;
+
     /** Constructor. */
     public AttributeFilterContext() {
         prefilteredAttributes = new HashMap<String, IdPAttribute>();
@@ -225,26 +233,54 @@ public final class AttributeFilterContext extends BaseContext {
     /**
      * Get the strategy used to locate the SP's metadata context.
      * 
-     * @return Returns the requesterMetadataContextLookupStrategy.
+     * @return lookup strategy
      */
-    @NonnullAfterInit public Function<AttributeFilterContext, SAMLMetadataContext>
-            getRequesterMetadataContextLookupStrategy() {
+    @NonnullAfterInit
+    public Function<AttributeFilterContext, SAMLMetadataContext> getRequesterMetadataContextLookupStrategy() {
         return requesterMetadataContextLookupStrategy;
     }
 
     /**
      * Set the strategy used to locate the SP's metadata context.
      * 
-     * @param strategy The requesterMetadataContextLookupStrategy to set.
+     * @param strategy lookup strategy
      */
     public void setRequesterMetadataContextLookupStrategy(
-            @Nonnull final Function<AttributeFilterContext, SAMLMetadataContext> strategy) {
+            @Nonnull final Function<AttributeFilterContext,SAMLMetadataContext> strategy) {
         requesterMetadataContextLookupStrategy =
                 Constraint.isNotNull(strategy, "MetadataContext lookup strategy cannot be null");
     }
 
-    /** Get the Requester Metadata context.<br/> This value is cached and so only calculated once.
-     * @return the cached context
+    /**
+     * Get the strategy used to locate the {@link ProxiedRequesterContext}.
+     * 
+     * @return  lookup strategy
+     * 
+     * @since 3.4.0
+     */
+    @NonnullAfterInit
+    public Function<AttributeFilterContext,ProxiedRequesterContext> getProxiedRequesterContextLookupStrategy() {
+        return proxiedRequesterContextLookupStrategy;
+    }
+
+    /**
+     * Set the strategy used to locate the SP's metadata context.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 3.4.0
+     */
+    public void setProxiedRequesterContextLookupStrategy(
+            @Nonnull final Function<AttributeFilterContext,ProxiedRequesterContext> strategy) {
+        proxiedRequesterContextLookupStrategy =
+                Constraint.isNotNull(strategy, "ProxiedRequesterContext lookup strategy cannot be null");
+    }
+
+    /** Get the Requester Metadata context.
+     * 
+     * <p>This value is cached and so only calculated once.</p>
+     * 
+     * @return the context
      */
     @Nullable public SAMLMetadataContext getRequesterMetadataContext() {
         if (null == requesterMetadataContext && null != requesterMetadataContextLookupStrategy) {
@@ -253,4 +289,19 @@ public final class AttributeFilterContext extends BaseContext {
         return requesterMetadataContext;
     }
     
+    /** Get the {@link ProxiedRequesterContext}.
+     * 
+     * <p>This value is cached and so only calculated once.</p>
+     * 
+     * @return the context
+     * 
+     * @since 3.4.0
+     */
+    @Nullable public ProxiedRequesterContext getProxiedRequesterContext() {
+        if (null == proxiedRequesterContext && null != proxiedRequesterContextLookupStrategy) {
+            proxiedRequesterContext = proxiedRequesterContextLookupStrategy.apply(this);
+        }
+        return proxiedRequesterContext;
+    }
+
 }
\ No newline at end of file
diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterPolicyRule.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterPolicyRule.java
new file mode 100644
index 0000000..da7ada5
--- /dev/null
+++ b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterPolicyRule.java
@@ -0,0 +1,68 @@
+/*
+ * 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.attribute.filter.policyrule.filtercontext.impl;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
+import net.shibboleth.idp.attribute.filter.policyrule.impl.AbstractStringPolicyRule;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+
+import org.opensaml.profile.context.ProxiedRequesterContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Compare a proxied attribute requester's entity ID for this resolution with the provided name.
+ * 
+ * @since 3.4.0
+ */
+public class ProxiedRequesterPolicyRule extends AbstractStringPolicyRule {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ProxiedRequesterPolicyRule.class);
+
+    /**
+     * Compare the requester from the context with the provided string.
+     * 
+     * @param filterContext the context
+     * @return whether it matches
+     * 
+     *         {@inheritDoc}
+     */
+    @Override public Tristate matches(@Nonnull final AttributeFilterContext filterContext) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+        final ProxiedRequesterContext ctx = filterContext.getProxiedRequesterContext();
+        if (null == ctx) {
+            return Tristate.FALSE;
+        }
+
+        if (isIgnoreCase()) {
+            for (final String requester : ctx.getRequesters()) {
+                if (stringCompare(requester) == Tristate.TRUE) {
+                    return Tristate.TRUE;
+                }
+            }
+            return Tristate.FALSE;
+        } else {
+            return ctx.getRequesters().contains(getMatchString()) ? Tristate.TRUE : Tristate.FALSE;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterRegexpPolicyRule.java b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterRegexpPolicyRule.java
new file mode 100644
index 0000000..63af1e9
--- /dev/null
+++ b/idp-attribute-filter-impl/src/main/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterRegexpPolicyRule.java
@@ -0,0 +1,64 @@
+/*
+ * 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.attribute.filter.policyrule.filtercontext.impl;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
+import net.shibboleth.idp.attribute.filter.policyrule.impl.AbstractRegexpPolicyRule;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+
+import org.opensaml.profile.context.ProxiedRequesterContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Compare a proxied attribute requester's entity ID for this resolution with the provided regexp.
+ * 
+ * @since 3.4.0
+ */
+public class ProxiedRequesterRegexpPolicyRule extends AbstractRegexpPolicyRule {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ProxiedRequesterRegexpPolicyRule.class);
+
+    /**
+     * Compare the requester from the context with the provided regexp.
+     * 
+     * @param filterContext the context
+     * @return whether it matches
+     * 
+     *         {@inheritDoc}
+     */
+    @Override public Tristate matches(@Nonnull final AttributeFilterContext filterContext) {
+        ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+        final ProxiedRequesterContext ctx = filterContext.getProxiedRequesterContext();
+        if (null == ctx) {
+            return Tristate.FALSE;
+        }
+
+        for (final String requester : ctx.getRequesters()) {
+            if (regexpCompare(requester) == Tristate.TRUE) {
+                return Tristate.TRUE;
+            }
+        }
+        
+        return Tristate.FALSE;
+    }
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterPolicyRuleTest.java b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterPolicyRuleTest.java
new file mode 100644
index 0000000..a1fcc71
--- /dev/null
+++ b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterPolicyRuleTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.attribute.filter.policyrule.filtercontext.impl;
+
+import net.shibboleth.idp.attribute.filter.PolicyRequirementRule.Tristate;
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
+import net.shibboleth.idp.attribute.filter.matcher.impl.DataSources;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
+
+import java.util.Arrays;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProxiedRequesterContext;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for {@link ProxiedRequesterPolicyRule}.
+ */
+public class ProxiedRequesterPolicyRuleTest {
+
+    private ProxiedRequesterPolicyRule getMatcher(boolean caseSensitive) throws ComponentInitializationException {
+        final ProxiedRequesterPolicyRule matcher = new ProxiedRequesterPolicyRule();
+        matcher.setMatchString("requester");
+        matcher.setIgnoreCase(!caseSensitive);
+        matcher.setId("Test");
+        matcher.initialize();
+        return matcher;
+
+    }
+
+    private ProxiedRequesterPolicyRule getMatcher() throws ComponentInitializationException {
+        return getMatcher(true);
+    }
+
+    @Test public void testNull() throws ComponentInitializationException {
+
+        try {
+            new ProxiedRequesterPolicyRule().matches(null);
+            Assert.fail();
+        } catch (UninitializedComponentException ex) {
+            // OK
+        }
+    }
+
+    @Test public void testUnpopulated()
+            throws ComponentInitializationException {
+        Assert.assertEquals(getMatcher().matches(DataSources.unPopulatedFilterContext()), Tristate.FALSE);
+    }
+
+    @Test public void testNoProxies()
+            throws ComponentInitializationException {
+        Assert.assertEquals(getMatcher().matches(DataSources.populatedFilterContext(null, null, "foo")), Tristate.FALSE);
+    }
+
+    @Test public void testCaseSensitive() throws ComponentInitializationException {
+
+        final ProxiedRequesterPolicyRule matcher = getMatcher();
+        
+        final AttributeFilterContext ctx = DataSources.populatedFilterContext(null, null, "wibble");
+        ctx.setProxiedRequesterContextLookupStrategy(
+                new ChildContextLookup<AttributeFilterContext,ProxiedRequesterContext>(ProxiedRequesterContext.class));
+        ctx.getSubcontext(ProxiedRequesterContext.class, true).getRequesters().addAll(Arrays.asList("foo", "bar"));
+
+        Assert.assertEquals(matcher.matches(ctx), Tristate.FALSE);
+        
+        ctx.getSubcontext(ProxiedRequesterContext.class).getRequesters().add("requester");
+        Assert.assertEquals(matcher.matches(ctx), Tristate.TRUE);
+    }
+
+    @Test public void testCaseInsensitive() throws ComponentInitializationException {
+
+        final ProxiedRequesterPolicyRule matcher = getMatcher(false);
+        
+        final AttributeFilterContext ctx = DataSources.populatedFilterContext(null, null, "wibble");
+        ctx.setProxiedRequesterContextLookupStrategy(
+                new ChildContextLookup<AttributeFilterContext,ProxiedRequesterContext>(ProxiedRequesterContext.class));
+        ctx.getSubcontext(ProxiedRequesterContext.class, true).getRequesters().addAll(Arrays.asList("foo", "bar"));
+
+        Assert.assertEquals(matcher.matches(ctx), Tristate.FALSE);
+        
+        ctx.getSubcontext(ProxiedRequesterContext.class).getRequesters().add("REQUESTER");
+        Assert.assertEquals(matcher.matches(ctx), Tristate.TRUE);
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterRegexpPolicyRuleTest.java b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterRegexpPolicyRuleTest.java
new file mode 100644
index 0000000..aad7249
--- /dev/null
+++ b/idp-attribute-filter-impl/src/test/java/net/shibboleth/idp/attribute/filter/policyrule/filtercontext/impl/ProxiedRequesterRegexpPolicyRuleTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.attribute.filter.policyrule.filtercontext.impl;
+
+import net.shibboleth.idp.attribute.filter.PolicyRequirementRule.Tristate;
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
+import net.shibboleth.idp.attribute.filter.matcher.impl.DataSources;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.UninitializedComponentException;
+
+import java.util.Arrays;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProxiedRequesterContext;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for {@link ProxiedRequesterRegexpPolicyRule}.
+ */
+public class ProxiedRequesterRegexpPolicyRuleTest {
+    
+    private ProxiedRequesterRegexpPolicyRule getMatcher() throws ComponentInitializationException {
+        final ProxiedRequesterRegexpPolicyRule matcher = new ProxiedRequesterRegexpPolicyRule();
+        matcher.setRegularExpression("^requ.*");
+        matcher.setId("Test");
+        matcher.initialize();
+        return matcher;
+    }
+
+    @Test public void testNull() throws ComponentInitializationException {
+
+        try {
+            new ProxiedRequesterRegexpPolicyRule().matches(null);
+            Assert.fail();
+        } catch (UninitializedComponentException ex) {
+            // OK
+        }
+    }
+
+    @Test public void testUnpopulated()
+            throws ComponentInitializationException {
+        Assert.assertEquals(getMatcher().matches(DataSources.unPopulatedFilterContext()), Tristate.FALSE);
+    }
+
+    @Test public void testNoRequester()
+            throws ComponentInitializationException {
+        Assert.assertEquals(getMatcher().matches(DataSources.populatedFilterContext(null, null, "foo")), Tristate.FALSE);
+    }
+
+    @Test public void testAll() throws ComponentInitializationException {
+
+        final ProxiedRequesterRegexpPolicyRule matcher = getMatcher();
+        
+        final AttributeFilterContext ctx = DataSources.populatedFilterContext(null, null, "wibble");
+        ctx.setProxiedRequesterContextLookupStrategy(
+                new ChildContextLookup<AttributeFilterContext,ProxiedRequesterContext>(ProxiedRequesterContext.class));
+        ctx.getSubcontext(ProxiedRequesterContext.class, true).getRequesters().addAll(Arrays.asList("foo", "bar"));
+
+        Assert.assertEquals(matcher.matches(ctx), Tristate.FALSE);
+        
+        ctx.getSubcontext(ProxiedRequesterContext.class).getRequesters().add("requester");
+        Assert.assertEquals(matcher.matches(ctx), Tristate.TRUE);
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/impl/AttributeFilterNamespaceHandler.java b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/impl/AttributeFilterNamespaceHandler.java
index 8e1e46b..945d682 100644
--- a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/impl/AttributeFilterNamespaceHandler.java
+++ b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/impl/AttributeFilterNamespaceHandler.java
@@ -38,6 +38,8 @@ import net.shibboleth.idp.attribute.filter.spring.policyrule.impl.NumOfAttribute
 import net.shibboleth.idp.attribute.filter.spring.policyrule.impl.PredicateRuleParser;
 import net.shibboleth.idp.attribute.filter.spring.policyrule.impl.PrincipalNameRegexRuleParser;
 import net.shibboleth.idp.attribute.filter.spring.policyrule.impl.PrincipalNameRuleParser;
+import net.shibboleth.idp.attribute.filter.spring.policyrule.impl.ProxiedRequesterRegexRuleParser;
+import net.shibboleth.idp.attribute.filter.spring.policyrule.impl.ProxiedRequesterRuleParser;
 import net.shibboleth.idp.attribute.filter.spring.saml.impl.AttributeInMetadataRuleParser;
 import net.shibboleth.idp.attribute.filter.spring.saml.impl.AttributeRequesterEntityAttributeExactRuleParser;
 import net.shibboleth.idp.attribute.filter.spring.saml.impl.AttributeRequesterEntityAttributeRegexRuleParser;
@@ -96,6 +98,11 @@ public class AttributeFilterNamespaceHandler extends BaseSpringNamespaceHandler
         registerBeanDefinitionParser(AttributeRequesterRegexRuleParser.SCHEMA_TYPE_AFP,
                 new AttributeRequesterRegexRuleParser());
 
+        registerBeanDefinitionParser(ProxiedRequesterRuleParser.SCHEMA_TYPE_AFP, new ProxiedRequesterRuleParser());
+
+        registerBeanDefinitionParser(ProxiedRequesterRegexRuleParser.SCHEMA_TYPE_AFP,
+                new ProxiedRequesterRegexRuleParser());
+
         registerBeanDefinitionParser(AttributeIssuerRuleParser.SCHEMA_TYPE_AFP, new AttributeIssuerRuleParser());
 
         registerBeanDefinitionParser(AttributeIssuerRegexRuleParser.SCHEMA_TYPE_AFP,
diff --git a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/policyrule/impl/ProxiedRequesterRegexRuleParser.java b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/policyrule/impl/ProxiedRequesterRegexRuleParser.java
new file mode 100644
index 0000000..8bac980
--- /dev/null
+++ b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/policyrule/impl/ProxiedRequesterRegexRuleParser.java
@@ -0,0 +1,44 @@
+/*
+ * 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.attribute.filter.spring.policyrule.impl;
+
+import javax.annotation.Nonnull;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.idp.attribute.filter.policyrule.filtercontext.impl.ProxiedRequesterRegexpPolicyRule;
+import net.shibboleth.idp.attribute.filter.spring.BaseFilterParser;
+
+/**
+ * Bean definition parser for {@link ProxiedRequesterRegexpPolicyRule}.
+ */
+public class ProxiedRequesterRegexRuleParser extends AbstractRegexPolicyRuleParser {
+
+    /** Schema type. */
+    @Nonnull public static final QName SCHEMA_TYPE_AFP = new QName(BaseFilterParser.NAMESPACE, "ProxiedRequesterRegex");
+
+    /** {@inheritDoc} */
+    @Override protected QName getAFPName() {
+        return SCHEMA_TYPE_AFP;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nonnull protected Class<ProxiedRequesterRegexpPolicyRule> getNativeBeanClass() {
+        return ProxiedRequesterRegexpPolicyRule.class;
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/policyrule/impl/ProxiedRequesterRuleParser.java b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/policyrule/impl/ProxiedRequesterRuleParser.java
new file mode 100644
index 0000000..cbd1942
--- /dev/null
+++ b/idp-attribute-filter-spring/src/main/java/net/shibboleth/idp/attribute/filter/spring/policyrule/impl/ProxiedRequesterRuleParser.java
@@ -0,0 +1,44 @@
+/*
+ * 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.attribute.filter.spring.policyrule.impl;
+
+import javax.annotation.Nonnull;
+import javax.xml.namespace.QName;
+
+import net.shibboleth.idp.attribute.filter.policyrule.filtercontext.impl.ProxiedRequesterPolicyRule;
+import net.shibboleth.idp.attribute.filter.spring.BaseFilterParser;
+
+/**
+ * Bean definition parser for {@link ProxiedRequesterPolicyRule}.
+ */
+public class ProxiedRequesterRuleParser extends AbstractStringPolicyRuleParser {
+
+    /** Schema type. */
+    @Nonnull public static final QName SCHEMA_TYPE_AFP = new QName(BaseFilterParser.NAMESPACE, "ProxiedRequester");
+
+    /** {@inheritDoc} */
+    @Override protected QName getAFPName() {
+        return SCHEMA_TYPE_AFP;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nonnull protected Class<ProxiedRequesterPolicyRule> getNativeBeanClass() {
+        return ProxiedRequesterPolicyRule.class;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/policy/ProxiedRequesterRegexRuleParserTest.java b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/policy/ProxiedRequesterRegexRuleParserTest.java
new file mode 100644
index 0000000..ace4e5b
--- /dev/null
+++ b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/policy/ProxiedRequesterRegexRuleParserTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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.attribute.filter.spring.policy;
+
+import net.shibboleth.idp.attribute.filter.policyrule.filtercontext.impl.ProxiedRequesterRegexpPolicyRule;
+import net.shibboleth.idp.attribute.filter.spring.BaseAttributeFilterParserTest;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class ProxiedRequesterRegexRuleParserTest extends BaseAttributeFilterParserTest {
+
+
+    @Test public void policy() throws ComponentInitializationException {
+
+        final ProxiedRequesterRegexpPolicyRule arRule = (ProxiedRequesterRegexpPolicyRule) getPolicyRule("proxiedRegexRequester.xml", true);
+        Assert.assertEquals(arRule.getRegularExpression(), "^urn:example:.*$");
+    }
+ 
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/policy/ProxiedRequesterRuleParserTest.java b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/policy/ProxiedRequesterRuleParserTest.java
new file mode 100644
index 0000000..f0d5026
--- /dev/null
+++ b/idp-attribute-filter-spring/src/test/java/net/shibboleth/idp/attribute/filter/spring/policy/ProxiedRequesterRuleParserTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.attribute.filter.spring.policy;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.filter.Matcher;
+import net.shibboleth.idp.attribute.filter.PolicyRequirementRule;
+import net.shibboleth.idp.attribute.filter.PolicyRequirementRule.Tristate;
+import net.shibboleth.idp.attribute.filter.context.AttributeFilterContext;
+import net.shibboleth.idp.attribute.filter.matcher.impl.DataSources;
+import net.shibboleth.idp.attribute.filter.policyrule.filtercontext.impl.ProxiedRequesterPolicyRule;
+import net.shibboleth.idp.attribute.filter.spring.BaseAttributeFilterParserTest;
+import net.shibboleth.idp.attribute.resolver.ResolutionException;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProxiedRequesterContext;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ProxiedRequesterRuleParserTest extends BaseAttributeFilterParserTest {
+
+    private Map<String, IdPAttribute> epaUid;
+
+    @BeforeClass public void setupAttributes() throws ComponentInitializationException, ResolutionException {
+
+        epaUid = getAttributes("epa-uid.xml");
+    }
+
+    @Test public void policy() throws ComponentInitializationException {
+        policy("proxiedRequester.xml", true);
+    }
+
+    public void policy(String path, boolean isAfp) throws ComponentInitializationException {
+        final PolicyRequirementRule rule = getPolicyRule(path, isAfp);
+
+        AttributeFilterContext filterContext = DataSources.populatedFilterContext("principal", "issuer", "http://example.org");
+        filterContext.setProxiedRequesterContextLookupStrategy(
+                new ChildContextLookup<AttributeFilterContext,ProxiedRequesterContext>(ProxiedRequesterContext.class));
+        filterContext.getSubcontext(ProxiedRequesterContext.class, true).getRequesters().addAll(Arrays.asList("foo", "bar"));
+        
+        
+        Assert.assertEquals(rule.matches(filterContext), Tristate.FALSE);
+        filterContext.getSubcontext(ProxiedRequesterContext.class).getRequesters().add("https://service.example.edu/shibboleth-sp");
+        Assert.assertEquals(rule.matches(filterContext), Tristate.TRUE);
+
+        final ProxiedRequesterPolicyRule arRule = (ProxiedRequesterPolicyRule) rule;
+        Assert.assertEquals(arRule.getMatchString(), "https://service.example.edu/shibboleth-sp");
+        Assert.assertFalse(arRule.isIgnoreCase());
+    }
+ 
+    @Test public void matcher() throws ComponentInitializationException {
+        matcher("proxiedRequester.xml", true);
+    }
+
+    public void matcher(String path, boolean isAfp) throws ComponentInitializationException {
+        final Matcher matcher = getMatcher(path, isAfp);
+
+        AttributeFilterContext filterContext = DataSources.populatedFilterContext("principal", "issuer", "http://example.org");
+        filterContext.setProxiedRequesterContextLookupStrategy(
+                new ChildContextLookup<AttributeFilterContext,ProxiedRequesterContext>(ProxiedRequesterContext.class));
+        filterContext.getSubcontext(ProxiedRequesterContext.class, true).getRequesters().addAll(Arrays.asList("foo", "bar"));
+
+        filterContext.setPrefilteredIdPAttributes(epaUid.values());
+        Set<IdPAttributeValue<?>> result = matcher.getMatchingValues(epaUid.get("uid"), filterContext);
+        Assert.assertTrue(result.isEmpty());
+
+        filterContext.getSubcontext(ProxiedRequesterContext.class).getRequesters().add("https://service.example.edu/shibboleth-sp");
+        result = matcher.getMatchingValues(epaUid.get("uid"), filterContext);
+        Assert.assertEquals(result.size(), 1);
+        Assert.assertEquals(result.iterator().next().getValue(), "daffyDuck");
+    }
+    
+}
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/matcher/afp/proxiedRequester.xml b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/matcher/afp/proxiedRequester.xml
new file mode 100644
index 0000000..2c55d14
--- /dev/null
+++ b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/matcher/afp/proxiedRequester.xml
@@ -0,0 +1,4 @@
+<AttributeRule attributeID="email" xmlns="urn:mace:shibboleth:2.0:afp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
+    <PermitValueRule xsi:type="ProxiedRequester" value="https://service.example.edu/shibboleth-sp" />
+</AttributeRule>
\ No newline at end of file
diff --git a/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/afp/proxiedRegexRequester.xml b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/afp/proxiedRegexRequester.xml
new file mode 100644
index 0000000..c4d681b
--- /dev/null
+++ b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/afp/proxiedRegexRequester.xml
@@ -0,0 +1,4 @@
+<AttributeFilterPolicy id="MostBasicExample" xmlns="urn:mace:shibboleth:2.0:afp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
+    <PolicyRequirementRule xsi:type="ProxiedRequesterRegex" regex="^urn:example:.*$" />
+</AttributeFilterPolicy>
diff --git a/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/afp/proxiedRequester.xml b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/afp/proxiedRequester.xml
new file mode 100644
index 0000000..03b4392
--- /dev/null
+++ b/idp-attribute-filter-spring/src/test/resources/net/shibboleth/idp/attribute/filter/policyrule/afp/proxiedRequester.xml
@@ -0,0 +1,4 @@
+<AttributeFilterPolicy id="MostBasicExample" xmlns="urn:mace:shibboleth:2.0:afp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd" >
+	<PolicyRequirementRule xsi:type="ProxiedRequester" value="https://service.example.edu/shibboleth-sp" ignoreCase="false" />
+</AttributeFilterPolicy>
diff --git a/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/FilterAttributes.java b/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/FilterAttributes.java
index 200b98c..3f64f34 100644
--- a/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/FilterAttributes.java
+++ b/idp-profile-impl/src/main/java/net/shibboleth/idp/profile/impl/FilterAttributes.java
@@ -42,6 +42,7 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.messaging.context.navigate.RootContextLookup;
 import org.opensaml.profile.action.ActionSupport;
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.ProxiedRequesterContext;
 import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
 import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
 import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
@@ -101,6 +102,18 @@ public class FilterAttributes extends AbstractProfileAction {
      * Strategy used to locate the {@link SAMLMetadataContext} associated with a given {@link AttributeFilterContext}.
      */
     @Nonnull private Function<AttributeFilterContext,SAMLMetadataContext> metadataFromFilterLookupStrategy;
+
+    /**
+     * Strategy used to locate the {@link ProxiedRequesterContext} associated with a given
+     * {@link ProfileRequestContext}.
+     */
+    @Nonnull private Function<ProfileRequestContext,ProxiedRequesterContext> proxiedRequesterContextLookupStrategy;
+
+    /**
+     * Strategy used to locate the {@link ProxiedRequesterContext} associated with a given
+     * {@link AttributeFilterContext}.
+     */
+    @Nonnull private Function<AttributeFilterContext,ProxiedRequesterContext> proxiesFromFilterLookupStrategy;
     
     /** Whether to treat resolver errors as equivalent to resolving no attributes. */
     private boolean maskFailures;
@@ -136,17 +149,19 @@ public class FilterAttributes extends AbstractProfileAction {
                 new ChildContextLookup<>(SAMLMetadataContext.class),
                 Functions.compose(new ChildContextLookup<>(SAMLPeerEntityContext.class),
                         new InboundMessageContextLookup()));
-        
+                
         // This is always set to navigate to the root context and then apply the previous function.
-        metadataFromFilterLookupStrategy = Functions.compose(
-                new Function<ProfileRequestContext,SAMLMetadataContext>() {
-                    @Override
-                    public SAMLMetadataContext apply(final ProfileRequestContext input) {
-                        return metadataContextLookupStrategy.apply(input);
-                    }
-                },
+        metadataFromFilterLookupStrategy = Functions.compose(metadataContextLookupStrategy,
                 new RootContextLookup<AttributeFilterContext,ProfileRequestContext>());
 
+        // Default: inbound msg context -> child
+        proxiedRequesterContextLookupStrategy = Functions.compose(
+                new ChildContextLookup<>(ProxiedRequesterContext.class), new InboundMessageContextLookup());
+        
+        // This is always set to navigate to the root context and then apply the previous function.
+        proxiesFromFilterLookupStrategy = Functions.compose(proxiedRequesterContextLookupStrategy,
+                new RootContextLookup<AttributeFilterContext,ProfileRequestContext>());
+        
         // Defaults to ProfileRequestContext -> RelyingPartyContext -> AttributeFilterContext.
         filterContextCreationStrategy = Functions.compose(new ChildContextLookup<>(AttributeFilterContext.class, true),
                 new ChildContextLookup<ProfileRequestContext,RelyingPartyContext>(RelyingPartyContext.class));
@@ -246,10 +261,9 @@ public class FilterAttributes extends AbstractProfileAction {
     /**
      * Set the strategy used to locate the {@link SAMLMetadataContext} associated with a given
      * {@link ProfileRequestContext}.  Also sets the strategy to find the {@link SAMLMetadataContext}
-     * from the {@link AttributeFilterContext};  
-     * SAMLMetadataContext
-     * @param strategy strategy used to locate the {@link AuthenticationContext} associated with a given
-     *            {@link ProfileRequestContext}
+     * from the {@link AttributeFilterContext}.
+     * 
+     * @param strategy lookup strategy
      */
     public void setMetadataContextLookupStrategy(
             @Nonnull final Function<ProfileRequestContext,SAMLMetadataContext> strategy) {
@@ -262,6 +276,25 @@ public class FilterAttributes extends AbstractProfileAction {
     }
 
     /**
+     * Set the strategy used to locate the {@link ProxiedRequesterContext} associated with a given
+     * {@link ProfileRequestContext}. Also sets the strategy to find the {@link ProxiedRequesterContext}
+     * from the {@link AttributeFilterContext}.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 3.4.0
+     */
+    public void setProxiedRequesterContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,ProxiedRequesterContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        proxiedRequesterContextLookupStrategy =
+                Constraint.isNotNull(strategy, "ProxiedRequesterContext lookup strategy cannot be null");
+        proxiesFromFilterLookupStrategy = Functions.compose(proxiedRequesterContextLookupStrategy,
+                new RootContextLookup<AttributeFilterContext,ProfileRequestContext>());
+    }
+    
+    /**
      * Set whether to treat resolution failure as equivalent to resolving no attributes.
      * 
      * <p>This matches the behavior of V2.</p>
@@ -389,6 +422,7 @@ public class FilterAttributes extends AbstractProfileAction {
         }
                 
         filterContext.setRequesterMetadataContextLookupStrategy(metadataFromFilterLookupStrategy);
+        filterContext.setProxiedRequesterContextLookupStrategy(proxiesFromFilterLookupStrategy);
 
         // If the filter context doesn't have a set of attributes to filter already
         // then look for them in the AttributeContext.
diff --git a/idp-schema/src/main/resources/schema/shibboleth-afp.xsd b/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
index 9a4714d..6a818c7 100644
--- a/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
+++ b/idp-schema/src/main/resources/schema/shibboleth-afp.xsd
@@ -327,6 +327,17 @@
         </complexContent>
     </complexType>
 
+    <complexType name="ProxiedRequester">
+        <annotation>
+            <documentation>
+                A match function that matches a proxied attribute requester against the specified value.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:StringMatchType"/>
+        </complexContent>
+    </complexType>
+
     <complexType name="Issuer">
         <annotation>
             <documentation>
@@ -427,6 +438,17 @@
         </complexContent>
     </complexType>
 
+    <complexType name="ProxiedRequesterRegex">
+        <annotation>
+            <documentation>
+                A match function that matches a proxied attribute requester against the specified regular expression.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="afp:RegexMatchType"/>
+        </complexContent>
+    </complexType>
+
     <complexType name="IssuerRegex">
         <annotation>
             <documentation>

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


More information about the commits mailing list