View Javadoc

1   /*
2    * Copyright [2006] [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.resolver.provider.attributeDefinition;
18  
19  import java.io.StringWriter;
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.velocity.Template;
28  import org.apache.velocity.VelocityContext;
29  import org.apache.velocity.app.VelocityEngine;
30  import org.apache.velocity.runtime.resource.util.StringResourceRepository;
31  import org.opensaml.xml.util.DatatypeHelper;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
36  import edu.internet2.middleware.shibboleth.common.attribute.provider.BasicAttribute;
37  import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
38  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
39  import edu.internet2.middleware.shibboleth.common.util.StringResourceLoader;
40  
41  /**
42   * An {@link AttributeDefinition} that constructs its values based on the values of its dependencies using the Velocity
43   * Template Language. Dependencies may have multiple values, however multiples dependencies must have the same number of
44   * values. In the case of multi-valued dependencies, the template will be evaluated multiples times, iterating over each
45   * dependency.
46   */
47  public class TemplateAttributeDefinition extends BaseAttributeDefinition {
48  
49      /** Class logger. */
50      private final Logger log = LoggerFactory.getLogger(TemplateAttributeDefinition.class);
51  
52      /** Velocity engine to use to render attribute values. */
53      private VelocityEngine velocity;
54  
55      /** Name the attribute template is registered under within the template engine. */
56      private String templateName;
57  
58      /** Template that produces the attribute value. */
59      private String attributeTemplate;
60  
61      /**
62       * IDs of the attributes used in this composite.
63       */
64      private List<String> sourceAttributes;
65  
66      /**
67       * Constructor.
68       * 
69       * @param newVelocityEngine velocity engine used to parse template.
70       */
71      public TemplateAttributeDefinition(VelocityEngine newVelocityEngine) {
72          velocity = newVelocityEngine;
73          sourceAttributes = new ArrayList<String>();
74      }
75  
76      /** {@inheritDoc} */
77      protected BaseAttribute doResolve(ShibbolethResolutionContext resolutionContext)
78              throws AttributeResolutionException {
79          Map<String, Iterator> sourceValues = new HashMap<String, Iterator>();
80          BasicAttribute<Object> attribute = new BasicAttribute<Object>();
81          attribute.setId(getId());
82  
83          int valueCount = -1;
84  
85          // get source attributes values
86          for (String attributeId : sourceAttributes) {
87              Collection values = getValuesFromAllDependencies(resolutionContext, attributeId);
88  
89              if (valueCount == -1) {
90                  valueCount = values.size();
91              } else if (valueCount != values.size()) {
92                  log.error("All attributes used in TemplateAttributeDefinition " + getId()
93                          + " must have the same number of values.");
94                  throw new AttributeResolutionException("All attributes used in TemplateAttributeDefinition " + getId()
95                          + " must have the same number of values.");
96              }
97  
98              sourceValues.put(attributeId, values.iterator());
99          }
100 
101         // build velocity context
102         VelocityContext vCtx = new VelocityContext();
103         vCtx.put("requestContext", resolutionContext.getAttributeRequestContext());
104         for (int i = 0; i < valueCount; i++) {
105             for (String attributeId : sourceValues.keySet()) {
106                 vCtx.put(attributeId, sourceValues.get(attributeId).next());
107             }
108 
109             try {
110                 log.debug("Populating the following {} template", templateName);
111 
112                 StringWriter output = new StringWriter();
113                 Template template = velocity.getTemplate(templateName);
114                 template.merge(vCtx, output);
115                 attribute.getValues().add(output.toString());
116             } catch (Exception e) {
117                 log.error("Unable to populate " + templateName + " template", e);
118                 throw new AttributeResolutionException("Unable to evaluate template", e);
119             }
120         }
121 
122         return attribute;
123     }
124 
125     /**
126      * Initialize the attribute definition and prepare it for use.
127      * 
128      * @throws Exception if unable to initialize attribute definition
129      */
130     public void initialize() throws Exception {
131         if (DatatypeHelper.isEmpty(attributeTemplate)) {
132             StringBuffer defaultTemplate = new StringBuffer();
133             for (String id : sourceAttributes) {
134                 defaultTemplate.append("${").append(id).append("} ");
135             }
136             attributeTemplate = defaultTemplate.toString();
137         }
138 
139         registerTemplate();
140     }
141 
142     /**
143      * Registers the template with template engine.
144      */
145     protected void registerTemplate() {
146         StringResourceRepository repository = StringResourceLoader.getRepository();
147         templateName = "shibboleth.resolver.ad." + getId();
148         repository.putStringResource(templateName, attributeTemplate.trim());
149     }
150 
151     /** {@inheritDoc} */
152     public void validate() throws AttributeResolutionException {
153         // do nothing
154     }
155 
156     /**
157      * Get the attribute template.
158      * 
159      * @return the attribute template
160      */
161     public String getAttributeTemplate() {
162         return attributeTemplate;
163     }
164 
165     /**
166      * Set the attribute template.
167      * 
168      * @param newAttributeTemplate the attribute template
169      */
170     public void setAttributeTemplate(String newAttributeTemplate) {
171         attributeTemplate = newAttributeTemplate;
172     }
173 
174     /**
175      * Get the source attribute IDs.
176      * 
177      * @return the source attribute IDs
178      */
179     public List<String> getSourceAttributes() {
180         return sourceAttributes;
181     }
182 
183     /**
184      * Set the source attribute IDs.
185      * 
186      * @param newSourceAttributes the source attribute IDs
187      */
188     public void setSourceAttributes(List<String> newSourceAttributes) {
189         sourceAttributes = newSourceAttributes;
190     }
191 }