Skip to content

Commit

Permalink
XWIKI-6936: Cannot save a class after creating a property named 'acti…
Browse files Browse the repository at this point in the history
…on' (#1292)

 * Fixed by limiting the actions accepted in the filter to the one explicitly defined in the xaction parameters if it exists.
 * Added an ActionFilter unit test
  • Loading branch information
manuelleduc committed May 7, 2020
1 parent d8872c1 commit d7562e6
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 58 deletions.
Expand Up @@ -75,6 +75,7 @@
*#
#macro(editActionButton $action $resourceIdentifier $class)
#submitButton("action_${action}", $services.localization.render("core.shortcuts.edit.${resourceIdentifier}"), $services.localization.render($resourceIdentifier), $class)
<input type="hidden" name="xaction" value="$escapetool.xml($action)" />
#end

#**
Expand Down
Expand Up @@ -21,6 +21,8 @@

import java.io.IOException;
import java.util.Enumeration;
import java.util.Objects;
import java.util.stream.Stream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
Expand Down Expand Up @@ -88,12 +90,20 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
{
// Only HTTP requests can be dispatched.
if (request instanceof HttpServletRequest
&& !Boolean.valueOf((String) request.getAttribute(ATTRIBUTE_ACTION_DISPATCHED))) {
&& !Boolean.parseBoolean((String) request.getAttribute(ATTRIBUTE_ACTION_DISPATCHED)))
{
HttpServletRequest hrequest = (HttpServletRequest) request;
Enumeration<String> parameterNames = hrequest.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameter = parameterNames.nextElement();
if (parameter.startsWith(ACTION_PREFIX)) {

// If some xactions are passed as parameter, the parameters prefixed with 'action_' are only taken into
// account if they are part of the xaction list. Otherwise, all the parameters prefixed with 'action_'
// are accepted.
String[] xactions = request.getParameterValues("xaction");
if (parameter.startsWith(ACTION_PREFIX) && (xactions == null || Stream.of(xactions)
.anyMatch(it -> Objects.equals(parameter, String.format("action_%s", it)))))
{
String targetURL = getTargetURL(hrequest, parameter);
RequestDispatcher dispatcher = hrequest.getRequestDispatcher(targetURL);
if (dispatcher != null) {
Expand Down Expand Up @@ -154,7 +164,8 @@ private String getTargetURL(HttpServletRequest request, String action)
Utils.getComponent(ConfigurationSource.class, XWikiCfgConfigurationSource.ROLEHINT);
if ("1".equals(configuration.getProperty("xwiki.virtual.usepath", "1"))) {
if (servletPath.equals(PATH_SEPARATOR
+ configuration.getProperty("xwiki.virtual.usepath.servletpath", "wiki"))) {
+ configuration.getProperty("xwiki.virtual.usepath.servletpath", "wiki")))
{
// Move the wiki name together with the servlet path
servletPath += path.substring(0, index);
index = path.indexOf(PATH_SEPARATOR, index + 1);
Expand Down
@@ -0,0 +1,172 @@
package com.xpn.xwiki.web;/*
* 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.
*/

import java.util.Collections;

import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.junit.jupiter.api.Test;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.test.annotation.BeforeComponent;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.mockito.MockitoComponentManager;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* Tests of {@link ActionFilter}.
*
* @version $Id$
* @since 12.4RC1
*/
@ComponentTest
public class ActionFilterTest
{
private final ActionFilter filter = new ActionFilter();

@BeforeComponent
public void setup(MockitoComponentManager componentManager) throws Exception
{
Utils.setComponentManager(componentManager);
componentManager.registerMockComponent(ConfigurationSource.class, "xwikicfg");
componentManager.registerComponent(ComponentManager.class, "context", componentManager);
}

@Test
void doFilterNotAnHttpServletRequest() throws Exception
{
ServletRequest request = mock(ServletRequest.class);
ServletResponse response = mock(ServletResponse.class);
FilterChain chain = mock(FilterChain.class);

filter.doFilter(request, response, chain);

verify(request, never()).getParameterValues("xaction");
verify(chain).doFilter(request, response);
}

@Test
void doFilterActionDispatcherIsTrue() throws Exception
{
ServletRequest request = mock(HttpServletRequest.class);
ServletResponse response = mock(ServletResponse.class);
FilterChain chain = mock(FilterChain.class);

when(request.getAttribute(ActionFilter.class.getName() + ".actionDispatched")).thenReturn("true");

filter.doFilter(request, response, chain);

verify(request, never()).getParameterValues("xaction");
verify(chain).doFilter(request, response);
}

@Test
void doFilterXactionNull() throws Exception
{
HttpServletRequest request = mock(HttpServletRequest.class);
ServletResponse response = mock(ServletResponse.class);
FilterChain chain = mock(FilterChain.class);

when(request.getParameterValues("xaction")).thenReturn(null);
when(request.getParameterNames()).thenReturn(Collections.enumeration(Collections.singletonList("a")));

filter.doFilter(request, response, chain);

verify(request).getParameterValues("xaction");
verify(chain).doFilter(request, response);
verify(request, never()).getRequestDispatcher(any());
}

@Test
void doFilterXactionNullParamActionExist() throws Exception
{
HttpServletRequest request = mock(HttpServletRequest.class);
ServletResponse response = mock(ServletResponse.class);
FilterChain chain = mock(FilterChain.class);

when(request.getParameterValues("xaction")).thenReturn(null);
when(request.getParameterNames()).thenReturn(Collections.enumeration(Collections.singletonList("action_a")));
when(request.getRequestURI()).thenReturn("/segment1/segment2/segment3/");
when(request.getServletPath()).thenReturn("/serv");
when(request.getContextPath()).thenReturn("/ctx/");

filter.doFilter(request, response, chain);

verify(request).getParameterValues("xaction");
verify(chain).doFilter(request, response);
verify(request).getRequestDispatcher(any());
}

@Test
void doFilterXactionIsAction() throws Exception
{
HttpServletRequest request = mock(HttpServletRequest.class);
ServletResponse response = mock(ServletResponse.class);
FilterChain chain = mock(FilterChain.class);

when(request.getParameterValues("xaction")).thenReturn(new String[] {
"a"
});

when(request.getParameterNames()).thenReturn(Collections.enumeration(Collections.singletonList("action_a")));

when(request.getRequestURI()).thenReturn("/segment1/segment2/segment3/");
when(request.getServletPath()).thenReturn("/serv");
when(request.getContextPath()).thenReturn("/ctx/");

filter.doFilter(request, response, chain);

verify(request).getParameterValues("xaction");
verify(chain).doFilter(request, response);
verify(request).getRequestDispatcher(any());
}

@Test
void doFilterXactionIsNotAction() throws Exception
{
HttpServletRequest request = mock(HttpServletRequest.class);
ServletResponse response = mock(ServletResponse.class);
FilterChain chain = mock(FilterChain.class);

when(request.getParameterValues("xaction")).thenReturn(new String[] {
"b"
});

when(request.getParameterNames()).thenReturn(Collections.enumeration(Collections.singletonList("action_a")));

when(request.getRequestURI()).thenReturn("/segment1/segment2/segment3/");
when(request.getServletPath()).thenReturn("/serv");
when(request.getContextPath()).thenReturn("/ctx/");

filter.doFilter(request, response, chain);

verify(request).getParameterValues("xaction");
verify(chain).doFilter(request, response);
verify(request, never()).getRequestDispatcher(any());
}
}
Expand Up @@ -1222,6 +1222,7 @@ $html
*#
#macro(submitButton $name $shortcut $value)
<span class="buttonwrapper"><input class="button" type="submit" name="$name"#if($keyboardShortcutsEnabled) title="$shortcut"#end value="$value"/></span>
<input type="hidden" name="xaction" value="$escapetool.xml($action)" />
#end
#**
* Displays a submit button for the editor. This macro calls submitButton,
Expand Down
Expand Up @@ -39,6 +39,13 @@
</properties>
<modules>
<module>xwiki-platform-xclass-test-pageobjects</module>
<module>xwiki-platform-xclass-test-tests</module>
</modules>
<profiles>
<profile>
<id>docker</id>
<modules>
<module>xwiki-platform-xclass-test-docker</module>
</modules>
</profile>
</profiles>
</project>
Expand Up @@ -27,17 +27,10 @@
<artifactId>xwiki-platform-xclass-test</artifactId>
<version>12.4-SNAPSHOT</version>
</parent>
<artifactId>xwiki-platform-xclass-test-tests</artifactId>
<name>XWiki Platform - XClass - Tests - Functional Tests</name>
<!-- TODO: Move to use "functional-test" in the future when http://jira.codehaus.org/browse/MNG-1911 is fixed,
see https://jira.xwiki.org/browse/XWIKI-7683 -->
<artifactId>xwiki-platform-xclass-test-docker</artifactId>
<name>XWiki Platform - XClass - Tests - Functional Docker Tests</name>
<packaging>jar</packaging>
<description>Tests for the XClass Application</description>
<properties>
<xwikiCfgSuperadminPassword>pass</xwikiCfgSuperadminPassword>
<!-- Functional tests are allowed to output content to the console -->
<xwiki.surefire.captureconsole.skip>true</xwiki.surefire.captureconsole.skip>
</properties>
<description>Tests for the XClass Application with Docker</description>
<dependencies>
<dependency>
<groupId>org.xwiki.platform</groupId>
Expand All @@ -48,7 +41,7 @@
<!-- Test dependencies -->
<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-test-ui</artifactId>
<artifactId>xwiki-platform-test-docker</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
Expand All @@ -62,20 +55,14 @@
<build>
<testSourceDirectory>src/test/it</testSourceDirectory>
<plugins>
<!-- TODO: Remove when the http://jira.codehaus.org/browse/MNG-1911 is fixed, see also
https://jira.xwiki.org/browse/XWIKI-7683 -->
<plugin>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-tool-packager-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<phase>generate-test-resources</phase>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<includes>
<include>**/AllIT.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
Expand All @@ -92,22 +79,22 @@
</dependencies>
<build>
<plugins>
<!-- Add the Clover JAR to the Packager plugin runtime classpath since the Packager plugin uses java classes
that have been instrumented with Clover (XWiki oldcore for example) -->
<plugin>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-tool-packager-plugin</artifactId>
<version>${project.version}</version>
<dependencies>
<dependency>
<groupId>org.openclover</groupId>
<artifactId>clover</artifactId>
<version>${clover.version}</version>
</dependency>
</dependencies>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemProperties combine.children="append">
<!-- Tell the Docker-based test to activate the Clover profile so that the Clover JAR is added to
WEB-INF/lib -->
<property>
<name>xwiki.test.ui.profiles</name>
<value>clover</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
</project>
@@ -0,0 +1,40 @@
/*
* 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.xclass.test.ui;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.xwiki.test.docker.junit5.UITest;

/**
* All UI tests for the XClasses.
*
* @version $Id$
* @since 12.4RC1
*/
@UITest
public class AllIT
{
@Nested
@DisplayName("Class Sheet UI tests")
class NestedClassSheetIT extends ClassSheetIT
{
}
}

0 comments on commit d7562e6

Please sign in to comment.