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