View Javadoc

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