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.util.Map;
21  
22  import javax.script.Compilable;
23  import javax.script.CompiledScript;
24  import javax.script.ScriptContext;
25  import javax.script.ScriptEngine;
26  import javax.script.ScriptEngineManager;
27  import javax.script.ScriptException;
28  import javax.script.SimpleScriptContext;
29  
30  import org.opensaml.xml.util.DatatypeHelper;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
35  import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
36  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ResolutionPlugIn;
37  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.ShibbolethResolutionContext;
38  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.DataConnector;
39  
40  /**
41   * An attribute definition the computes the attribute definition by executing a script written in some JSR-223
42   * supporting language.
43   */
44  public class ScriptedAttributeDefinition extends BaseAttributeDefinition {
45  
46      /** Class logger. */
47      private final Logger log = LoggerFactory.getLogger(ScriptedAttributeDefinition.class);
48  
49      /** The scripting language. */
50      private String scriptLanguage;
51  
52      /** The script to execute. */
53      private String script;
54  
55      /** The script engine to execute the script. */
56      private ScriptEngine scriptEngine;
57  
58      /** The compiled form of the script, if the script engine supports compiling. */
59      private CompiledScript compiledScript;
60  
61      /**
62       * Constructor.
63       * 
64       * @param language the scripting language that will be used
65       */
66      public ScriptedAttributeDefinition(String language) {
67          scriptLanguage = language;
68      }
69  
70      /**
71       * Gets the scripting language used.
72       * 
73       * @return scripting language used
74       */
75      public String getScriptLanguage() {
76          return scriptLanguage;
77      }
78  
79      /**
80       * Gets the script that will be executed.
81       * 
82       * @return script that will be executed
83       */
84      public String getScript() {
85          return script;
86      }
87  
88      /**
89       * Sets the script that will be executed.
90       * 
91       * @param newScript script that will be executed
92       */
93      public void setScript(String newScript) {
94          script = newScript;
95          compileScript();
96      }
97  
98      /** Initializes this attribute definition. */
99      public void initialize() {
100         ScriptEngineManager sem = new ScriptEngineManager();
101         scriptEngine = sem.getEngineByName(scriptLanguage);
102         compileScript();
103     }
104 
105     /** {@inheritDoc} */
106     public void validate() throws AttributeResolutionException {
107         if (scriptEngine == null) {
108             log.error("ScriptletAttributeDefinition " + getId()
109                     + " unable to create scripting engine for the language: " + scriptLanguage);
110             throw new AttributeResolutionException("ScriptletAttributeDefinition " + getId()
111                     + " unable to create scripting engine for the language: " + scriptLanguage);
112         }
113     }
114 
115     /** {@inheritDoc} */
116     protected BaseAttribute<?> doResolve(ShibbolethResolutionContext resolutionContext)
117             throws AttributeResolutionException {
118         ScriptContext context = getScriptContext(resolutionContext);
119 
120         try {
121             if (compiledScript != null) {
122                 compiledScript.eval(context);
123             } else {
124                 scriptEngine.eval(script, context);
125             }
126 
127             BaseAttribute attribute = (BaseAttribute) context.getAttribute(getId());
128             if (attribute == null) {
129                 log.error("{} produced a null attribute", getId());
130                 throw new AttributeResolutionException(getId() + " produced a null attributes");
131             }
132 
133             return attribute;
134         } catch (ScriptException e) {
135             log.error("ScriptletAttributeDefinition " + getId() + " unable to execute script", e);
136             throw new AttributeResolutionException("ScriptletAttributeDefinition " + getId()
137                     + " unable to execute script", e);
138         }
139     }
140 
141     /** Compiles the script if the scripting engine supports it. */
142     protected void compileScript() {
143         if (DatatypeHelper.isEmpty(script)) {
144             return;
145         }
146 
147         try {
148             if (scriptEngine != null && scriptEngine instanceof Compilable) {
149                 compiledScript = ((Compilable) scriptEngine).compile(script);
150             }
151         } catch (ScriptException e) {
152             compiledScript = null;
153             log.warn("{} unable to compile even though the scripting engine supports this functionality.", getId());
154         }
155     }
156 
157     /**
158      * Creates the script execution context from the resolution context.
159      * 
160      * @param resolutionContext current resolution context
161      * 
162      * @return constructed script context
163      * 
164      * @throws AttributeResolutionException thrown if dependent data connectors or attribute definitions can not be
165      *             resolved
166      */
167     protected ScriptContext getScriptContext(ShibbolethResolutionContext resolutionContext)
168             throws AttributeResolutionException {
169         SimpleScriptContext scriptContext = new SimpleScriptContext();
170         scriptContext.setAttribute(getId(), null, ScriptContext.ENGINE_SCOPE);
171 
172         scriptContext.setAttribute("requestContext", resolutionContext.getAttributeRequestContext(),
173                 ScriptContext.ENGINE_SCOPE);
174 
175         ResolutionPlugIn plugin;
176         Map<String, BaseAttribute> attributes;
177         BaseAttribute attribute;
178 
179         if (!getDependencyIds().isEmpty()) {
180             for (String dependency : getDependencyIds()) {
181                 plugin = resolutionContext.getResolvedPlugins().get(dependency);
182                 if (plugin instanceof DataConnector) {
183                     attributes = ((DataConnector) plugin).resolve(resolutionContext);
184                     if (attributes != null) {
185                         for (BaseAttribute attr : attributes.values()) {
186                             scriptContext.setAttribute(attr.getId(), attr, ScriptContext.ENGINE_SCOPE);
187                         }
188                     }
189                 } else if (plugin instanceof AttributeDefinition) {
190                     attribute = ((AttributeDefinition) plugin).resolve(resolutionContext);
191                     if (attribute != null) {
192                         scriptContext.setAttribute(attribute.getId(), attribute, ScriptContext.ENGINE_SCOPE);
193                     }
194                 }
195             }
196         }
197 
198         return scriptContext;
199     }
200 }