View Javadoc

1   /*
2    * Licensed to the University Corporation for Advanced Internet Development, 
3    * Inc. (UCAID) under one or more contributor license agreements.  See the 
4    * NOTICE file distributed with this work for additional information regarding
5    * copyright ownership. The UCAID licenses this file to You under the Apache 
6    * License, Version 2.0 (the "License"); you may not use this file except in 
7    * compliance with the License.  You may obtain a copy of the License at
8    *
9    *    http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package edu.internet2.middleware.shibboleth.common.attribute.filtering.provider.match.saml;
19  
20  import java.util.List;
21  
22  import org.opensaml.saml2.core.Attribute;
23  import org.opensaml.saml2.metadata.EntityDescriptor;
24  import org.opensaml.samlext.saml2mdattr.EntityAttributes;
25  import org.opensaml.xml.XMLObject;
26  import org.opensaml.xml.schema.XSAny;
27  import org.opensaml.xml.schema.XSString;
28  import org.opensaml.xml.util.DatatypeHelper;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import edu.internet2.middleware.shibboleth.common.attribute.filtering.provider.FilterProcessingException;
33  import edu.internet2.middleware.shibboleth.common.attribute.filtering.provider.ShibbolethFilteringContext;
34  import edu.internet2.middleware.shibboleth.common.attribute.filtering.provider.match.basic.AbstractMatchFunctor;
35  
36  /**
37   * Base class for match functions that check whether a particular entity attribute is present and contains a given
38   * value.
39   */
40  public abstract class AbstractEntityAttributeMatchFunctor extends AbstractMatchFunctor {
41  
42      /** Class logger. */
43      private final Logger log = LoggerFactory.getLogger(AbstractEntityAttributeMatchFunctor.class);
44  
45      /** The name of the entity attribute the entity must have. */
46      private String name;
47  
48      /** The name format of the entity attribute the entity must have. */
49      private String nameFormat;
50  
51      /**
52       * Gets the name of the entity attribute the entity must have.
53       * 
54       * @return name of the entity attribute the entity must have
55       */
56      public String getName() {
57          return name;
58      }
59  
60      /**
61       * Sets the name of the entity attribute the entity must have.
62       * 
63       * @param attributeName name of the entity attribute the entity must have
64       */
65      public void setName(String attributeName) {
66          name = attributeName;
67      }
68  
69      /**
70       * Gets the name format of the entity attribute the entity must have.
71       * 
72       * @return name format of the entity attribute the entity must have
73       */
74      public String getNameFormat() {
75          return nameFormat;
76      }
77  
78      /**
79       * Sets the name format of the entity attribute the entity must have.
80       * 
81       * @param attributeNameFormat name format of the entity attribute the entity must have
82       */
83      public void setNameFormat(String attributeNameFormat) {
84          nameFormat = DatatypeHelper.safeTrimOrNullString(attributeNameFormat);
85      }
86  
87      /** {@inheritDoc} */
88      protected boolean doEvaluatePolicyRequirement(ShibbolethFilteringContext filterContext)
89              throws FilterProcessingException {
90          return hasEntityAttribute(filterContext);
91      }
92  
93      /** {@inheritDoc} */
94      protected boolean doEvaluateValue(ShibbolethFilteringContext filterContext, String attributeId,
95              Object attributeValue) throws FilterProcessingException {
96          return hasEntityAttribute(filterContext);
97      }
98  
99      /**
100      * Checks to see if the entity returned by {@link #getEntityMetadata(ShibbolethFilteringContext)} contains the
101      * entity attribute specified by this functor's configuration.
102      * 
103      * @param filterContext current request context
104      * 
105      * @return true if the entity has the configured attribute, false otherwise
106      */
107     protected boolean hasEntityAttribute(ShibbolethFilteringContext filterContext) {
108         EntityDescriptor entityDescriptor = getEntityMetadata(filterContext);
109         if (entityDescriptor == null) {
110             log.debug("No metadata available for the entity");
111             return false;
112         }
113 
114         Attribute entityAttribute = getEntityAttribute(entityDescriptor);
115         if (entityAttribute == null) {
116             return false;
117         }
118 
119         List<XMLObject> attributeValues = entityAttribute.getAttributeValues();
120         if (attributeValues == null || attributeValues.isEmpty()) {
121             log.debug("Entity attribute {} for entity {} does not contain any values", getName(),
122                     entityDescriptor.getEntityID());
123             return false;
124         }
125 
126         log.debug("Checking if entity attribute {} contains the required value.", getName());
127         String valueString;
128         for (XMLObject attributeValue : attributeValues) {
129             if (attributeValue instanceof XSAny) {
130                 valueString = ((XSAny) attributeValue).getTextContent();
131             } else if (attributeValue instanceof XSString) {
132                 valueString = ((XSString) attributeValue).getValue();
133             } else {
134                 log.debug("Entity attribute {} contains the unsupported value type {}, skipping it", getName(),
135                         attributeValue.getClass().getName());
136                 continue;
137             }
138 
139             if (valueString != null) {
140                 if (entityAttributeValueMatches(valueString)) {
141                     log.debug("Entity attribute {} value {} meets matching requirements", getName(), valueString);
142                     return true;
143                 }
144                 log.debug("Entity attribute {} value {} does not meet matching requirements", getName(), valueString);
145             }
146         }
147 
148         return false;
149     }
150 
151     /**
152      * Gets the entity descriptor for the entity to check.
153      * 
154      * @param filterContext current filter request context
155      * 
156      * @return entity descriptor for the entity to check
157      */
158     protected abstract EntityDescriptor getEntityMetadata(ShibbolethFilteringContext filterContext);
159 
160     /**
161      * Gets the entity attribute from the given entity metadata. If both the attribute name and name format for this
162      * match functor is configured then both must match, otherwise only the attribute name must match.
163      * 
164      * @param entityDescriptor the metadata for the entity
165      * 
166      * @return the entity or null if the metadata does not contain such an entity attribute
167      */
168     protected Attribute getEntityAttribute(EntityDescriptor entityDescriptor) {
169         List<XMLObject> entityAttributesCollection = entityDescriptor.getExtensions().getUnknownXMLObjects(
170                 EntityAttributes.DEFAULT_ELEMENT_NAME);
171         if (entityAttributesCollection == null || entityAttributesCollection.isEmpty()) {
172             log.debug("Descriptor for {} does not contain any EntityAttributes", entityDescriptor.getEntityID());
173             return null;
174         }
175 
176         if (entityAttributesCollection.size() > 1) {
177             log.debug("Descriptor for {} contains more than EntityAttributes extension, only using the first one",
178                     entityDescriptor.getEntityID());
179         }
180 
181         List<Attribute> entityAttributes = ((EntityAttributes) entityAttributesCollection.get(0)).getAttributes();
182         if (entityAttributes == null || entityAttributes.isEmpty()) {
183             log.debug("EntityAttributes extension for {} does not contain any Attributes",
184                     entityDescriptor.getEntityID());
185             return null;
186         }
187 
188         for (Attribute entityAttribute : entityAttributes) {
189             if (!DatatypeHelper.safeEquals(getName(), entityAttribute.getName())) {
190                 continue;
191             }
192 
193             if (getNameFormat() == null
194                     || (DatatypeHelper.safeEquals(getNameFormat(), entityAttribute.getNameFormat()))) {
195                 log.debug("Descriptor for {} contains an entity attribute with the name {} and the format {}",
196                         new Object[] { entityDescriptor.getEntityID(), getName(), getNameFormat() });
197                 return entityAttribute;
198             }
199         }
200 
201         log.debug("Descriptor for {} does not contain an entity attribute with the name {} and the format {}",
202                 new Object[] { entityDescriptor.getEntityID(), getName(), getNameFormat() });
203         return null;
204     }
205 
206     /**
207      * Checks whether the given entity attribute value matches the rules for particular implementation of this functor.
208      * 
209      * @param entityAttributeValue the entity attribute value, never null
210      * 
211      * @return true if the value matches, false if not
212      */
213     protected abstract boolean entityAttributeValueMatches(String entityAttributeValue);
214 }