/* * ============================================================================= * * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ============================================================================= */ package org.thymeleaf.standard.expression; import java.util.Map; import ognl.OgnlContext; import ognl.OgnlException; import ognl.PropertyAccessor; import ognl.enhance.UnsupportedCompilationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.IContext; /** * <p> * Implementation of {@code PropertyAccessor} that allows OGNL to access the contents of {@link IContext} * implementations as if they were a Map. * </p> * <p> * Note this accessor also has to take care of variable access restrictions (like e.g. forbidding access to * the request parameters in unescaped text or in preprocessing expressions). * </p> * <p> * Note a class with this name existed since 2.0, but it was completely reimplemented * in Thymeleaf 3.0 * </p> * * @author Michal Kreuzman * @author Daniel Fernández * @see PropertyAccessor * @since 3.0.0 */ public final class OGNLContextPropertyAccessor implements PropertyAccessor { private static final Logger LOGGER = LoggerFactory.getLogger(OGNLContextPropertyAccessor.class); public static final String RESTRICT_REQUEST_PARAMETERS = "%RESTRICT_REQUEST_PARAMETERS%"; static final String REQUEST_PARAMETERS_RESTRICTED_VARIABLE_NAME = "param"; OGNLContextPropertyAccessor() { super(); } public Object getProperty(final Map ognlContext, final Object target, final Object name) throws OgnlException { if (!(target instanceof IContext)) { throw new IllegalStateException( "Wrong target type. This property accessor is only usable for " + IContext.class.getName() + " implementations, and " + "in this case the target object is " + (target == null? "null" : ("of class " + target.getClass().getName()))); } if (REQUEST_PARAMETERS_RESTRICTED_VARIABLE_NAME.equals(name) && ognlContext != null && ognlContext.containsKey(RESTRICT_REQUEST_PARAMETERS)) { throw new OgnlException( "Access to variable \"" + name + "\" is forbidden in this context. Note some restrictions apply to " + "variable access. For example, accessing request parameters is forbidden in preprocessing and " + "unescaped expressions, and also in fragment inclusion specifications."); } final String propertyName = (name == null? null : name.toString()); // 'execInfo' translation from context variable to expression object - deprecated and to be removed in 3.1 final Object execInfoResult = checkExecInfo(propertyName, ognlContext); if (execInfoResult != null) { return execInfoResult; } /* * NOTE we do not check here whether we are being asked for the 'locale', 'request', 'response', etc. * because there already are specific expression objects for the most important of them, which should * be used instead: #locale, #httpServletRequest, #httpSession, etc. * The variables maps should just be used as a map, without exposure of its more-internal methods... */ final IContext context = (IContext) target; return context.getVariable(propertyName); } /** * Translation from 'execInfo' context variable (${execInfo}) to 'execInfo' expression object (${#execInfo}), needed * since 3.0.0. * * Note this is expressed as a separate method in order to mark this as deprecated and make it easily locatable. * * @param propertyName the name of the property being accessed (we are looking for 'execInfo'). * @param context the expression context, which should contain the expression objects. * @deprecated created (and deprecated) in 3.0.0 in order to support automatic conversion of calls to the 'execInfo' * context variable (${execInfo}) into the 'execInfo' expression object (${#execInfo}), which is its * new only valid form. This method, along with the infrastructure for execInfo conversion in * StandardExpressionUtils#mightNeedExpressionObjects(...) will be removed in 3.1. */ @Deprecated private static Object checkExecInfo(final String propertyName, final Map<String,Object> context) { if ("execInfo".equals(propertyName)) { LOGGER.warn( "[THYMELEAF][{}] Found Thymeleaf Standard Expression containing a call to the context variable " + "\"execInfo\" (e.g. \"${execInfo.templateName}\"), which has been deprecated. The " + "Execution Info should be now accessed as an expression object instead " + "(e.g. \"${#execInfo.templateName}\"). Deprecated use is still allowed, but will be removed " + "in future versions of Thymeleaf.", TemplateEngine.threadIndex()); return context.get("execInfo"); } return null; } public void setProperty(final Map context, final Object target, final Object name, final Object value) throws OgnlException { // IVariablesMap implementations should never be set values from OGNL expressions throw new UnsupportedOperationException("Cannot set values into VariablesMap instances from OGNL Expressions"); } public String getSourceAccessor(final OgnlContext context, final Object target, final Object index) { // This method is called during OGNL's bytecode enhancement optimizations in order to determine better- // performing methods to access the properties of an object. It's been written trying to mimic // what is done at MapPropertyAccessor#getSourceAccessor() method, removing all the parts related to indexed // access, which do not apply to IVariablesMap implementations. context.setCurrentAccessor(IContext.class); context.setCurrentType(Object.class); return ".getVariable(" + index + ")"; } public String getSourceSetter(final OgnlContext context, final Object target, final Object index) { // This method is called during OGNL's bytecode enhancement optimizations in order to determine better- // performing methods to access the properties of an object. Given IVariablesMap implementations should never // be set any values from OGNL, this exception should never be thrown anyway. throw new UnsupportedCompilationException( "Setting expression for " + context.getCurrentObject() + " with index of " + index + " cannot " + "be computed. IVariablesMap implementations are considered read-only by OGNL."); } }