### Eclipse Workspace Patch 1.0 #P xwiki-core-velocity Index: src/main/java/org/xwiki/velocity/internal/DefaultVelocityEngine.java =================================================================== --- src/main/java/org/xwiki/velocity/internal/DefaultVelocityEngine.java (revision 32200) +++ src/main/java/org/xwiki/velocity/internal/DefaultVelocityEngine.java (working copy) @@ -39,6 +39,7 @@ import org.xwiki.component.annotation.Requirement; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; import org.xwiki.component.logging.AbstractLogEnabled; +import org.xwiki.component.manager.ComponentManager; import org.xwiki.container.ApplicationContext; import org.xwiki.container.Container; import org.xwiki.container.servlet.ServletApplicationContext; @@ -85,6 +86,12 @@ * See the comment in {@link #init(org.apache.velocity.runtime.RuntimeServices)}. */ private RuntimeServices rsvc; + + /** + * Component Manager used to retrieve components by lookup. + * */ + @Requirement + private ComponentManager componentManager; /** Counter for the number of active rendering processes using each namespace. */ private final Map namespaceUsageCount = new HashMap(); @@ -287,6 +294,7 @@ // included Velocity templates are added to the current document before // evaluation instead of doing 2 separate executions. this.rsvc = runtimeServices; + this.rsvc.setApplicationAttribute("COMPONENT_MANAGER", this.componentManager); } /** Index: src/main/java/org/xwiki/velocity/introspection/RequiredAccesRight.java =================================================================== --- src/main/java/org/xwiki/velocity/introspection/RequiredAccesRight.java (revision 0) +++ src/main/java/org/xwiki/velocity/introspection/RequiredAccesRight.java (revision 0) @@ -0,0 +1,19 @@ +package org.xwiki.velocity.introspection; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates right needed to run a specific method. + * + * @version $Id: $ + * @since + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface RequiredAccesRight +{ + /** + * The right default value. + */ + String value() default ""; +} Index: src/main/java/org/xwiki/velocity/internal/DefaultVelocityConfiguration.java =================================================================== --- src/main/java/org/xwiki/velocity/internal/DefaultVelocityConfiguration.java (revision 32200) +++ src/main/java/org/xwiki/velocity/internal/DefaultVelocityConfiguration.java (working copy) @@ -37,6 +37,7 @@ import org.xwiki.velocity.VelocityConfiguration; import org.xwiki.velocity.introspection.ChainingUberspector; import org.xwiki.velocity.introspection.DeprecatedCheckUberspector; +import org.xwiki.velocity.introspection.RightsCheckUberspector; import org.xwiki.velocity.tools.RegexTool; /** @@ -54,6 +55,11 @@ private static final String PREFIX = "velocity."; /** + * Separator between chain classes. + */ + private static final String SEPARATOR = ","; + + /** * Defines from where to read the rendering configuration data. */ @Requirement @@ -95,7 +101,8 @@ // Prevents users from writing dangerous Velocity code like using Class.forName or Java threading APIs. this.defaultProperties.setProperty("runtime.introspector.uberspect", ChainingUberspector.class.getName()); this.defaultProperties.setProperty("runtime.introspector.uberspect.chainClasses", - SecureUberspector.class.getName() + "," + DeprecatedCheckUberspector.class.getName()); + SecureUberspector.class.getName() + SEPARATOR + DeprecatedCheckUberspector.class.getName() + SEPARATOR + + RightsCheckUberspector.class.getName()); } /** Index: src/test/java/org/xwiki/velocity/internal/DefaultVelocityConfigurationTest.java =================================================================== --- src/test/java/org/xwiki/velocity/internal/DefaultVelocityConfigurationTest.java (revision 32200) +++ src/test/java/org/xwiki/velocity/internal/DefaultVelocityConfigurationTest.java (working copy) @@ -32,10 +32,11 @@ import org.xwiki.test.annotation.MockingRequirement; import org.xwiki.velocity.introspection.ChainingUberspector; import org.xwiki.velocity.introspection.DeprecatedCheckUberspector; +import org.xwiki.velocity.introspection.RightsCheckUberspector; /** * Unit tests for {@link DefaultVelocityConfiguration}. - * + * * @version $Id$ * @since 2.4RC1 */ @@ -50,12 +51,15 @@ public void configure() throws Exception { final ConfigurationSource source = getComponentManager().lookup(ConfigurationSource.class); - getMockery().checking(new Expectations() {{ - allowing(source).getProperty("velocity.tools", Properties.class); - will(returnValue(Collections.emptyMap())); - allowing(source).getProperty("velocity.properties", Properties.class); - will(returnValue(Collections.emptyMap())); - }}); + getMockery().checking(new Expectations() + { + { + allowing(source).getProperty("velocity.tools", Properties.class); + will(returnValue(Collections.emptyMap())); + allowing(source).getProperty("velocity.properties", Properties.class); + will(returnValue(Collections.emptyMap())); + } + }); } @Test @@ -71,7 +75,8 @@ // Verify that the secure uberspector is set by default Assert.assertEquals(ChainingUberspector.class.getName(), this.configuration.getProperties().getProperty("runtime.introspector.uberspect")); - Assert.assertEquals(SecureUberspector.class.getName() + "," + DeprecatedCheckUberspector.class.getName(), + Assert.assertEquals(SecureUberspector.class.getName() + "," + DeprecatedCheckUberspector.class.getName() + "," + + RightsCheckUberspector.class.getName(), this.configuration.getProperties().getProperty("runtime.introspector.uberspect.chainClasses")); // Verify that null values are allowed by default Index: src/main/java/org/xwiki/velocity/introspection/RightsCheckUberspector.java =================================================================== --- src/main/java/org/xwiki/velocity/introspection/RightsCheckUberspector.java (revision 0) +++ src/main/java/org/xwiki/velocity/introspection/RightsCheckUberspector.java (revision 0) @@ -0,0 +1,157 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + * + */ +package org.xwiki.velocity.introspection; + +import java.lang.reflect.Method; + +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.RuntimeServicesAware; +import org.apache.velocity.util.introspection.Info; +import org.apache.velocity.util.introspection.Introspector; +import org.apache.velocity.util.introspection.Uberspect; +import org.apache.velocity.util.introspection.UberspectLoggable; +import org.apache.velocity.util.introspection.VelMethod; +import org.apache.velocity.util.introspection.VelPropertyGet; +import org.apache.velocity.util.introspection.VelPropertySet; +import org.xwiki.bridge.DocumentAccessBridge; +import org.xwiki.component.manager.ComponentLookupException; +import org.xwiki.component.manager.ComponentManager; + +/** + * Chainable Velocity Uberspector that checks for need rights when running a method. It does that by checking if the + * returned method has a RequiredAccessRights annotation. Because this is a chainable uberspector, it has to re-get the + * method using a default introspector, which is not safe; future uberspectors might not be able to return a precise + * method name, or a method of the original target object. + * + * @since + * @version $Id: $ + * @see ChainableUberspector + */ +public class RightsCheckUberspector extends AbstractChainableUberspector implements Uberspect, ChainableUberspector, + UberspectLoggable, RuntimeServicesAware +{ + /** + * Identifies programming rights. + */ + private static final String PROGRAMMING = "programming"; + + /** + * Document Access Bridge used to retrieve whether there are programming rights. + */ + private DocumentAccessBridge documentAccessBridge; + + /** + * Runtime services used to get document acces bridge. + */ + private RuntimeServices rs; + + /** + * {@inheritDoc} + * + * @see Uberspect#init() + */ + public void init() + { + super.init(); + this.introspector = new Introspector(log); + try { + this.documentAccessBridge = + ((ComponentManager) this.rs.getApplicationAttribute("COMPONENT_MANAGER")) + .lookup(DocumentAccessBridge.class); + } catch (ComponentLookupException e) { + e.printStackTrace(); + } + } + + /** + * {@inheritDoc} + * + * @see Uberspect#getMethod(java.lang.Object, java.lang.String, java.lang.Object[], + * org.apache.velocity.util.introspection.Info) + */ + @Override + public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception + { + VelMethod method = super.getMethod(obj, methodName, args, i); + if (method != null) { + Method m = introspector.getMethod(obj.getClass(), method.getMethodName(), args); + if (m != null && m.isAnnotationPresent(RequiredAccesRight.class)) { + RequiredAccesRight rightAnnotation = m.getAnnotation(RequiredAccesRight.class); + if (rightAnnotation.value().equals(PROGRAMMING) && !documentAccessBridge.hasProgrammingRights()) { + return null; + } + } + } + return method; + } + + /** + * {@inheritDoc} + * + * @see Uberspect#getPropertyGet(java.lang.Object, java.lang.String, org.apache.velocity.util.introspection.Info) + */ + @Override + public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception + { + VelPropertyGet method = super.getPropertyGet(obj, identifier, i); + if (method != null) { + Method m = introspector.getMethod(obj.getClass(), method.getMethodName(), new Object[] {}); + if (m != null && m.isAnnotationPresent(RequiredAccesRight.class)) { + RequiredAccesRight rightAnnotation = m.getAnnotation(RequiredAccesRight.class); + if (rightAnnotation.value().equals(PROGRAMMING) && !documentAccessBridge.hasProgrammingRights()) { + return null; + } + } + } + return method; + } + + /** + * {@inheritDoc} + * + * @see Uberspect#getPropertySet(java.lang.Object, java.lang.String, java.lang.Object, + * org.apache.velocity.util.introspection.Info) + */ + @Override + public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) throws Exception + { + VelPropertySet method = super.getPropertySet(obj, identifier, arg, i); + if (method != null) { + Method m = introspector.getMethod(obj.getClass(), method.getMethodName(), new Object[] {}); + if (m != null && m.isAnnotationPresent(RequiredAccesRight.class)) { + RequiredAccesRight rightAnnotation = m.getAnnotation(RequiredAccesRight.class); + if (rightAnnotation.value().equals(PROGRAMMING) && !documentAccessBridge.hasProgrammingRights()) { + return null; + } + } + } + return method; + } + + /** + * {@inheritDoc} + * */ + public void setRuntimeServices(RuntimeServices rs) + { + this.rs = rs; + + } +} Index: pom.xml =================================================================== --- pom.xml (revision 32200) +++ pom.xml (working copy) @@ -65,6 +65,11 @@ ${project.version} + org.xwiki.platform + xwiki-core-bridge + ${project.version} + + javax.servlet servlet-api 2.4