[java-identity-provider] branch master updated: Optimize EntityAttributesPredicate with mapped attribute support.

Scott Cantor cantor.2 at osu.edu
Wed May 15 20:37:22 EDT 2019


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=177a330f7bcd19f78edca423dcdd48ecbaa79321

The following commit(s) were added to refs/heads/master by this push:
       new  177a330   Optimize EntityAttributesPredicate with mapped attribute support.
177a330 is described below

commit 177a330f7bcd19f78edca423dcdd48ecbaa79321
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed May 15 20:37:20 2019 -0400

    Optimize EntityAttributesPredicate with mapped attribute support.
---
 .../filter/impl/PredicateFilterParser.java         |   2 +-
 .../profile/logic/EntityAttributesPredicate.java   | 261 +++++++++++++++++++++
 .../impl/RelyingPartyConfigurationSupport.java     |   2 +-
 3 files changed, 263 insertions(+), 2 deletions(-)

diff --git a/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/filter/impl/PredicateFilterParser.java b/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/filter/impl/PredicateFilterParser.java
index 7b12965..2895e74 100644
--- a/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/filter/impl/PredicateFilterParser.java
+++ b/idp-profile-spring/src/main/java/net/shibboleth/idp/profile/spring/relyingparty/metadata/filter/impl/PredicateFilterParser.java
@@ -26,12 +26,12 @@ import javax.xml.namespace.QName;
 import net.shibboleth.ext.spring.util.SpringSupport;
 import net.shibboleth.idp.profile.spring.relyingparty.metadata.AbstractMetadataProviderParser;
 import net.shibboleth.idp.profile.spring.relyingparty.metadata.ScriptTypeBeanParser;
+import net.shibboleth.idp.saml.profile.logic.EntityAttributesPredicate;
 import net.shibboleth.utilities.java.support.logic.PredicateSupport;
 import net.shibboleth.utilities.java.support.logic.ScriptedPredicate;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 import net.shibboleth.utilities.java.support.xml.ElementSupport;
 
-import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate;
 import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate.Candidate;
 import org.opensaml.saml.common.profile.logic.EntityGroupNamePredicate;
 import org.opensaml.saml.common.profile.logic.EntityIdPredicate;
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/logic/EntityAttributesPredicate.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/logic/EntityAttributesPredicate.java
new file mode 100644
index 0000000..1897e8d
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/logic/EntityAttributesPredicate.java
@@ -0,0 +1,261 @@
+/*
+ * 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.saml.profile.logic;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.ScopedStringAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.utilities.java.support.annotation.ParameterName;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.Predicate;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Extended version of EntityAttributes-driven predicate that adds an optimization to check
+ * for mapped attributes in an {@link AttributesMapContainer} structure before backing off
+ * to brute force examination of the metadata by the superclass.
+ */
+public class EntityAttributesPredicate extends org.opensaml.saml.common.profile.logic.EntityAttributesPredicate {
+
+    /** Determines whether we can check the mapped container. */
+    private final boolean nonOptimized;
+    
+    /** Delimiter to build string form of scoped values. */
+    @Nonnull @NotEmpty private String scopeDelimiter = "@";
+    
+    /**
+     * Constructor.
+     * 
+     * @param candidates the {@link Candidate} criteria to check for
+     */
+    public EntityAttributesPredicate(
+            @Nonnull @NonnullElements @ParameterName(name="candidates") final Collection<Candidate> candidates) {
+        super(candidates);
+
+        // matchAll is false, so a single Candidate with no NameFormat might work.
+        nonOptimized = Iterables.all(candidates, c -> c.getNameFormat() != null);
+    }
+
+    /**
+     * 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 EntityAttributesPredicate(
+            @Nonnull @NonnullElements @ParameterName(name="candidates") final Collection<Candidate> candidates,
+            @ParameterName(name="trim") final boolean trim) {
+        super(candidates, trim);
+        
+        // matchAll is false, so a single Candidate with no NameFormat might work.
+        nonOptimized = Iterables.all(candidates, c -> c.getNameFormat() != null);
+    }
+    
+    /**
+     * 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 EntityAttributesPredicate(
+            @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);
+        
+        // matchAll is true, so a single Candidate with a NameFormat prevents optimization
+        nonOptimized = Iterables.any(candidates, c -> c.getNameFormat() != null);
+    }
+    
+    /**
+     * 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 (input == null) {
+            return false;
+        } else if (nonOptimized) {
+            return super.test(input);
+        }
+
+        final List<AttributesMapContainer> containerList =
+                input.getObjectMetadata().get(AttributesMapContainer.class);
+        if (null != containerList && !containerList.isEmpty() && containerList.get(0).get() != null &&
+                !containerList.get(0).get().isEmpty()) {
+            
+            // If we find a matching tag, we win. Each tag is treated in OR fashion.
+            final EntityAttributesMatcher matcher = new EntityAttributesMatcher(containerList.get(0).get());
+            
+            if (getMatchAll()) {
+                if (Iterables.all(getCandidates(), matcher::test)) {
+                    return true;
+                }
+            } else {
+                if (Iterables.tryFind(getCandidates(), matcher::test).isPresent()) {
+                    return true;
+                }
+            }
+        }
+        
+        return super.test(input);
+    }
+
+    /**
+     * Determines whether an {@link Candidate} criterion is satisfied by the mapped
+     * attributes in an entity's metadata.
+     */
+    private class EntityAttributesMatcher implements Predicate<Candidate> {
+        
+        /** Class logger. */
+        @Nonnull private final Logger log = LoggerFactory.getLogger(EntityAttributesPredicate.class);
+        
+        /** 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(@Nonnull final Candidate input) {
+            
+            if (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()) {
+                        final String cvalstr = attributeValueToString(cval);
+                        if (tagvalstr != null && cvalstr != null) {
+                            if (tagvalstr.equals(cvalstr)) {
+                                valflags[tagindex] = true;
+                                break;
+                            } else if (getTrimTags()) {
+                                if (tagvalstr.equals(cvalstr.trim())) {
+                                    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()) {
+                        final String cvalstr = attributeValueToString(cval);
+                        if (tagexps.get(tagindex) != null && cvalstr != null) {
+                            if (tagexps.get(tagindex).matcher(cvalstr).matches()) {
+                                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;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/relyingparty/impl/RelyingPartyConfigurationSupport.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/relyingparty/impl/RelyingPartyConfigurationSupport.java
index 4e8a007..def1d06 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/relyingparty/impl/RelyingPartyConfigurationSupport.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/relyingparty/impl/RelyingPartyConfigurationSupport.java
@@ -24,7 +24,6 @@ import javax.annotation.Nullable;
 
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.messaging.context.navigate.EntityDescriptorLookupFunction;
-import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate;
 import org.opensaml.saml.common.profile.logic.EntityAttributesPredicate.Candidate;
 import org.opensaml.saml.common.profile.logic.EntityGroupNamePredicate;
 import org.opensaml.saml.metadata.resolver.MetadataResolver;
@@ -33,6 +32,7 @@ import org.opensaml.saml.saml2.metadata.EntityDescriptor;
 import net.shibboleth.idp.profile.logic.RelyingPartyIdPredicate;
 import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
 import net.shibboleth.idp.saml.profile.context.navigate.SAMLMetadataContextLookupFunction;
+import net.shibboleth.idp.saml.profile.logic.EntityAttributesPredicate;
 import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
 import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.logic.StrategyIndirectedPredicate;

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


More information about the commits mailing list