View Javadoc

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