WW-5386 Delete deprecated FileUploadInterceptor

This commit is contained in:
Kusal Kithul-Godage
2024-07-26 17:02:47 +10:00
parent 9d624069aa
commit 1ecfbae465
5 changed files with 2 additions and 943 deletions
@@ -1,273 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.interceptor.ValidationAware;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.action.UploadedFilesAware;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
import org.apache.struts2.dispatcher.multipart.UploadedFile;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <!-- START SNIPPET: description -->
* <p>
* Interceptor that is based off of {@link MultiPartRequestWrapper}, which is automatically applied for any request that
* includes a file. It adds the following parameters, where [File Name] is the name given to the file uploaded by the
* HTML form:
* </p>
* <ul>
*
* <li>[File Name] : File - the actual File</li>
*
* <li>[File Name]ContentType : String - the content type of the file</li>
*
* <li>[File Name]FileName : String - the actual name of the file uploaded (not the HTML name)</li>
*
* </ul>
*
* <p>You can get access to these files by merely providing setters in your action that correspond to any of the three
* patterns above, such as setDocument(File document), setDocumentContentType(String contentType), etc.
* <br>See the example code section.
* </p>
*
* <p> This interceptor will add several field errors, assuming that the action implements {@link ValidationAware}.
* These error messages are based on several i18n values stored in struts-messages.properties, a default i18n file
* processed for all i18n requests. You can override the text of these messages by providing text for the following
* keys:
* </p>
*
* <ul>
*
* <li>struts.messages.error.uploading - a general error that occurs when the file could not be uploaded</li>
*
* <li>struts.messages.error.file.too.large - occurs when the uploaded file is too large</li>
*
* <li>struts.messages.error.content.type.not.allowed - occurs when the uploaded file does not match the expected
* content types specified</li>
*
* <li>struts.messages.error.file.extension.not.allowed - occurs when the uploaded file does not match the expected
* file extensions specified</li>
*
* </ul>
* <p>
* <!-- END SNIPPET: description -->
*
* <p><u>Interceptor parameters:</u></p>
* <p>
* <!-- START SNIPPET: parameters -->
*
* <ul>
*
* <li>maximumSize (optional) - the maximum size (in bytes) that the interceptor will allow a file reference to be set
* on the action. Note, this is <b>not</b> related to the various properties found in struts.properties.
* Default to approximately 2MB.</li>
*
* <li>allowedTypes (optional) - a comma separated list of content types (ie: text/html) that the interceptor will allow
* a file reference to be set on the action. If none is specified allow all types to be uploaded.</li>
*
* <li>allowedExtensions (optional) - a comma separated list of file extensions (ie: .html) that the interceptor will allow
* a file reference to be set on the action. If none is specified allow all extensions to be uploaded.</li>
* </ul>
* <p>
* <p>
* <!-- END SNIPPET: parameters -->
*
* <p><u>Extending the interceptor:</u></p>
* <p>
* <p>
* <p>
* <!-- START SNIPPET: extending -->
* <p>
* You can extend this interceptor and override the acceptFile method to provide more control over which files
* are supported and which are not.
* </p>
* <!-- END SNIPPET: extending -->
*
* <p><u>Example code:</u></p>
*
* <pre>
* <!-- START SNIPPET: example-configuration -->
* &lt;action name="doUpload" class="com.example.UploadAction"&gt;
* &lt;interceptor-ref name="fileUpload"/&gt;
* &lt;interceptor-ref name="basicStack"/&gt;
* &lt;result name="success"&gt;good_result.jsp&lt;/result&gt;
* &lt;/action&gt;
* <!-- END SNIPPET: example-configuration -->
* </pre>
* <p>
* <!-- START SNIPPET: multipart-note -->
* <p>
* You must set the encoding to <code>multipart/form-data</code> in the form where the user selects the file to upload.
* </p>
* <!-- END SNIPPET: multipart-note -->
*
* <pre>
* <!-- START SNIPPET: example-form -->
* &lt;s:form action="doUpload" method="post" enctype="multipart/form-data"&gt;
* &lt;s:file name="upload" label="File"/&gt;
* &lt;s:submit/&gt;
* &lt;/s:form&gt;
* <!-- END SNIPPET: example-form -->
* </pre>
* <p>
* And then in your action code you'll have access to the File object if you provide setters according to the
* naming convention documented in the start.
* </p>
*
* <pre>
* <!-- START SNIPPET: example-action -->
* package com.example;
*
* import java.io.File;
* import com.opensymphony.xwork2.ActionSupport;
*
* public UploadAction extends ActionSupport {
* private File file;
* private String contentType;
* private String filename;
*
* public void setUpload(File file) {
* this.file = file;
* }
*
* public void setUploadContentType(String contentType) {
* this.contentType = contentType;
* }
*
* public void setUploadFileName(String filename) {
* this.filename = filename;
* }
*
* public String execute() {
* //...
* return SUCCESS;
* }
* }
* <!-- END SNIPPET: example-action -->
* </pre>
*
* @deprecated since Struts 6.4.0, use {@link ActionFileUploadInterceptor} instead
*/
@Deprecated
public class FileUploadInterceptor extends AbstractFileUploadInterceptor {
private static final long serialVersionUID = -4764627478894962478L;
protected static final Logger LOG = LogManager.getLogger(FileUploadInterceptor.class);
/* (non-Javadoc)
* @see com.opensymphony.xwork2.interceptor.Interceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
*/
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext ac = invocation.getInvocationContext();
HttpServletRequest request = ac.getServletRequest();
if (!(request instanceof MultiPartRequestWrapper multiWrapper)) {
if (LOG.isDebugEnabled()) {
ActionProxy proxy = invocation.getProxy();
LOG.debug(getTextMessage(STRUTS_MESSAGES_BYPASS_REQUEST_KEY,
new String[]{proxy.getNamespace(), proxy.getActionName()})
);
}
return invocation.invoke();
}
Object action = invocation.getAction();
if (action instanceof UploadedFilesAware) {
LOG.debug("Ignoring action: {} implementing: {} as it will be handled by: {}",
invocation.getProxy().getActionName(),
UploadedFilesAware.class.getSimpleName(),
ActionFileUploadInterceptor.class.getSimpleName()
);
return invocation.invoke();
}
applyValidation(action, multiWrapper);
// bind allowed Files
Enumeration<String> fileParameterNames = multiWrapper.getFileParameterNames();
while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
// get the value of this input tag
String inputName = fileParameterNames.nextElement();
// get the content type
String[] contentType = multiWrapper.getContentTypes(inputName);
if (isNonEmpty(contentType)) {
// get the name of the file from the input tag
String[] fileName = multiWrapper.getFileNames(inputName);
if (isNonEmpty(fileName)) {
// get a File object for the uploaded File
UploadedFile[] files = multiWrapper.getFiles(inputName);
if (files != null && files.length > 0) {
List<UploadedFile> acceptedFiles = new ArrayList<>(files.length);
List<String> acceptedContentTypes = new ArrayList<>(files.length);
List<String> acceptedFileNames = new ArrayList<>(files.length);
String contentTypeName = inputName + "ContentType";
String fileNameName = inputName + "FileName";
for (int index = 0; index < files.length; index++) {
if (acceptFile(action, files[index], fileName[index], contentType[index], inputName)) {
acceptedFiles.add(files[index]);
acceptedContentTypes.add(contentType[index]);
acceptedFileNames.add(fileName[index]);
}
}
if (!acceptedFiles.isEmpty()) {
Map<String, Parameter> newParams = new HashMap<>();
newParams.put(inputName, new Parameter.File(inputName, acceptedFiles.toArray(new UploadedFile[0])));
newParams.put(contentTypeName, new Parameter.File(contentTypeName, acceptedContentTypes.toArray(new String[0])));
newParams.put(fileNameName, new Parameter.File(fileNameName, acceptedFileNames.toArray(new String[0])));
ac.getParameters().appendAll(newParams);
}
}
} else {
if (LOG.isWarnEnabled()) {
LOG.warn(getTextMessage(action, STRUTS_MESSAGES_INVALID_FILE_KEY, new String[]{inputName}));
}
}
} else {
if (LOG.isWarnEnabled()) {
LOG.warn(getTextMessage(action, STRUTS_MESSAGES_INVALID_CONTENT_TYPE_KEY, new String[]{inputName}));
}
}
}
// invoke action
return invocation.invoke();
}
}
@@ -57,7 +57,6 @@
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor"/>
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<interceptor name="actionFileUpload" class="org.apache.struts2.interceptor.ActionFileUploadInterceptor"/>
<interceptor name="i18n" class="org.apache.struts2.interceptor.I18nInterceptor"/>
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
@@ -114,12 +113,6 @@
<interceptor-ref name="workflow"/>
</interceptor-stack>
<!-- Sample file upload stack -->
<interceptor-stack name="fileUploadStack">
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
<!-- Action based file upload stack -->
<interceptor-stack name="actionFileUploadStack">
<interceptor-ref name="actionFileUpload"/>
@@ -169,7 +162,6 @@
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="actionFileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
@@ -208,7 +200,6 @@
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="actionFileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
@@ -1,659 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.DefaultLocaleProvider;
import com.opensymphony.xwork2.ValidationAwareSupport;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.action.UploadedFilesAware;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;
import org.apache.struts2.dispatcher.multipart.StrutsUploadedFile;
import org.apache.struts2.dispatcher.multipart.UploadedFile;
import org.assertj.core.util.Files;
import org.springframework.mock.web.MockHttpServletRequest;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test case for FileUploadInterceptor.
*/
public class FileUploadInterceptorTest extends StrutsInternalTestCase {
private static final UploadedFile EMPTY_FILE = new UploadedFile() {
@Override
public Long length() {
return 0L;
}
@Override
public String getName() {
return "";
}
@Override
public boolean isFile() {
return false;
}
@Override
public boolean delete() {
return false;
}
@Override
public String getAbsolutePath() {
return null;
}
@Override
public File getContent() {
return Files.newTemporaryFile();
}
@Override
public String getOriginalName() {
return null;
}
@Override
public String getContentType() {
return null;
}
};
private FileUploadInterceptor interceptor;
private File tempDir;
private MockHttpServletRequest request;
public void testAcceptFileWithEmptyAllowedTypesAndExtensions() {
// when allowed type is empty
ValidationAwareSupport validation = new ValidationAwareSupport();
boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename", "text/plain", "inputName");
assertTrue(ok);
assertTrue(validation.getFieldErrors().isEmpty());
assertFalse(validation.hasErrors());
}
public void testAcceptFileWithoutEmptyTypes() {
interceptor.setAllowedTypes("text/plain");
// when file is of allowed types
ValidationAwareSupport validation = new ValidationAwareSupport();
boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename.txt", "text/plain", "inputName");
assertTrue(ok);
assertTrue(validation.getFieldErrors().isEmpty());
assertFalse(validation.hasErrors());
// when file is not of allowed types
validation = new ValidationAwareSupport();
boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, "filename.html", "text/html", "inputName");
assertFalse(notOk);
assertFalse(validation.getFieldErrors().isEmpty());
assertTrue(validation.hasErrors());
}
public void testAcceptFileWithWildcardContent() {
interceptor.setAllowedTypes("text/*");
ValidationAwareSupport validation = new ValidationAwareSupport();
boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename.txt", "text/plain", "inputName");
assertTrue(ok);
assertTrue(validation.getFieldErrors().isEmpty());
assertFalse(validation.hasErrors());
interceptor.setAllowedTypes("text/h*");
validation = new ValidationAwareSupport();
boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, "filename.html", "text/plain", "inputName");
assertFalse(notOk);
assertFalse(validation.getFieldErrors().isEmpty());
assertTrue(validation.hasErrors());
}
public void testAcceptFileWithoutEmptyExtensions() {
interceptor.setAllowedExtensions(".txt");
// when file is of allowed extensions
ValidationAwareSupport validation = new ValidationAwareSupport();
boolean ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename.txt", "text/plain", "inputName");
assertTrue(ok);
assertTrue(validation.getFieldErrors().isEmpty());
assertFalse(validation.hasErrors());
// when file is not of allowed extensions
validation = new ValidationAwareSupport();
boolean notOk = interceptor.acceptFile(validation, EMPTY_FILE, "filename.html", "text/html", "inputName");
assertFalse(notOk);
assertFalse(validation.getFieldErrors().isEmpty());
assertTrue(validation.hasErrors());
//test with multiple extensions
interceptor.setAllowedExtensions(".txt,.lol");
validation = new ValidationAwareSupport();
ok = interceptor.acceptFile(validation, EMPTY_FILE, "filename.lol", "text/plain", "inputName");
assertTrue(ok);
assertTrue(validation.getFieldErrors().isEmpty());
assertFalse(validation.hasErrors());
}
public void testAcceptFileWithNoFile() {
interceptor.setAllowedTypes("text/plain");
// when file is not of allowed types
ValidationAwareSupport validation = new ValidationAwareSupport();
boolean notOk = interceptor.acceptFile(validation, null, "filename.html", "text/html", "inputName");
assertFalse(notOk);
assertFalse(validation.getFieldErrors().isEmpty());
assertTrue(validation.hasErrors());
List<String> errors = validation.getFieldErrors().get("inputName");
assertEquals(1, errors.size());
String msg = errors.get(0);
assertTrue(msg.startsWith("Error uploading:"));
assertTrue(msg.indexOf("inputName") > 0);
}
public void testAcceptFileWithMaxSize() throws Exception {
interceptor.setMaximumSize(10L);
// when file is not of allowed types
ValidationAwareSupport validation = new ValidationAwareSupport();
URL url = ClassLoaderUtil.getResource("log4j2.xml", FileUploadInterceptorTest.class);
File file = new File(new URI(url.toString()));
assertTrue("log4j2.xml should be in src/test folder", file.exists());
UploadedFile uploadedFile = StrutsUploadedFile.Builder.create(file)
.withContentType("text/html")
.withOriginalName("filename")
.build();
boolean notOk = interceptor.acceptFile(validation, uploadedFile, "filename", "text/html", "inputName");
assertFalse(notOk);
assertFalse(validation.getFieldErrors().isEmpty());
assertTrue(validation.hasErrors());
List<String> errors = validation.getFieldErrors().get("inputName");
assertEquals(1, errors.size());
String msg = errors.get(0);
// the error message should contain at least this test
assertThat(msg).contains(
"The file is too large to be uploaded",
"inputName",
"log4j2.xml",
"allowed mx size is 10"
);
}
public void testNoMultipartRequest() throws Exception {
MyFileupAction action = new MyFileupAction();
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("NoMultipart");
mai.setInvocationContext(ActionContext.getContext());
// if no multipart request it will bypass and execute it
assertEquals("NoMultipart", interceptor.intercept(mai));
}
public void testInvalidContentTypeMultipartRequest() throws Exception {
request.setContentType("multipart/form-data"); // not a multipart contentype
request.setMethod("post");
MyFileupAction action = container.inject(MyFileupAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext().withParameters(HttpParameters.create().build());
ActionContext.getContext().withServletRequest(createMultipartRequestMaxSize(2000));
interceptor.intercept(mai);
assertTrue(action.hasErrors());
}
public void testNoContentMultipartRequest() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data");
request.setContent(null); // there is no content
MyFileupAction action = container.inject(MyFileupAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
ActionContext.getContext().withParameters(HttpParameters.create().build());
ActionContext.getContext().withServletRequest(createMultipartRequestMaxSize(2000));
interceptor.intercept(mai);
assertTrue(action.hasErrors());
}
public void testSuccessUploadOfATextFileMultipartRequest() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = new MyFileupAction();
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
ActionContext.getContext().withServletRequest(createMultipartRequestMaxSize(2000));
interceptor.intercept(mai);
assertFalse(action.hasErrors());
HttpParameters parameters = mai.getInvocationContext().getParameters();
assertEquals(3, parameters.keySet().size());
UploadedFile[] files = (UploadedFile[]) parameters.get("file").getObject();
String[] fileContentTypes = parameters.get("fileContentType").getMultipleValues();
String[] fileRealFilenames = parameters.get("fileFileName").getMultipleValues();
assertNotNull(files);
assertNotNull(fileContentTypes);
assertNotNull(fileRealFilenames);
assertEquals(1, files.length);
assertEquals(1, fileContentTypes.length);
assertEquals(1, fileRealFilenames.length);
assertEquals("text/html", fileContentTypes[0]);
assertNotNull("deleteme.txt", fileRealFilenames[0]);
}
/**
* tests whether with multiple files sent with the same name, the ones with forbiddenTypes (see
* FileUploadInterceptor.setAllowedTypes(...) ) are sorted out.
*/
public void testMultipleAccept() throws Exception {
final String htmlContent = "<html><head></head><body>html content</body></html>";
final String plainContent = "plain content";
final String bondary = "simple boundary";
final String endline = "\r\n";
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("POST");
request.addHeader("Content-type", "multipart/form-data; boundary=" + bondary);
String content = encodeTextFile("test.html", "text/plain", plainContent) +
encodeTextFile("test1.html", "text/html", htmlContent) +
encodeTextFile("test2.html", "text/html", htmlContent) +
endline +
endline +
endline +
"--" +
bondary +
"--" +
endline;
request.setContent(content.getBytes());
assertTrue(JakartaServletFileUpload.isMultipartContent(request));
MyFileupAction action = new MyFileupAction();
container.inject(action);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext().withParameters(HttpParameters.create(param).build());
ActionContext.getContext().withServletRequest(createMultipartRequestMaxSize(2000));
interceptor.setAllowedTypes("text/html");
interceptor.intercept(mai);
HttpParameters parameters = mai.getInvocationContext().getParameters();
assertEquals(3, parameters.keySet().size());
UploadedFile[] files = (UploadedFile[]) parameters.get("file").getObject();
String[] fileContentTypes = parameters.get("fileContentType").getMultipleValues();
String[] fileRealFilenames = parameters.get("fileFileName").getMultipleValues();
assertNotNull(files);
assertNotNull(fileContentTypes);
assertNotNull(fileRealFilenames);
assertEquals("files accepted ", 2, files.length);
assertEquals(2, fileContentTypes.length);
assertEquals(2, fileRealFilenames.length);
assertEquals("text/html", fileContentTypes[0]);
assertNotNull("test1.html", fileRealFilenames[0]);
}
public void testUnacceptedNumberOfFiles() throws Exception {
final String htmlContent = "<html><head></head><body>html content</body></html>";
final String plainContent = "plain content";
final String boundary = "simple boundary";
final String endline = "\r\n";
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("POST");
request.addHeader("Content-type", "multipart/form-data; boundary=" + boundary);
String content = encodeTextFile("test.html", "text/plain", plainContent) +
encodeTextFile("test1.html", "text/html", htmlContent) +
encodeTextFile("test2.html", "text/html", htmlContent) +
encodeTextFile("test3.html", "text/html", htmlContent) +
endline +
"--" +
boundary +
"--" +
endline;
request.setContent(content.getBytes());
assertTrue(JakartaServletFileUpload.isMultipartContent(request));
MyFileupAction action = new MyFileupAction();
container.inject(action);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext()
.withParameters(HttpParameters.create(param).build())
.withServletRequest(createMultipartRequestMaxFiles());
interceptor.setAllowedTypes("text/html");
interceptor.intercept(mai);
HttpParameters parameters = mai.getInvocationContext().getParameters();
assertEquals(0, parameters.keySet().size());
assertEquals(1, action.getActionErrors().size());
assertEquals(
"Request exceeded allowed number of files! Permitted number of files is: 3!",
action.getActionErrors().iterator().next()
);
}
public void testMultipartRequestMaxFileSize() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = container.inject(MyFileupAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext()
.withParameters(HttpParameters.create(param).build())
.withServletRequest(createMultipartRequestMaxFileSize());
interceptor.intercept(mai);
assertTrue(action.hasActionErrors());
Collection<String> errors = action.getActionErrors();
assertEquals(1, errors.size());
String msg = errors.iterator().next();
// FIXME: the expected size is 40 - length of the string
assertEquals(
"File deleteme.txt assigned to file exceeded allowed size limit! Max size allowed is: 10 but file was: 11!",
msg);
}
public void testMultipartRequestMaxStringLength() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"normalFormField1\"\r\n" +
"\r\n" +
"it works" +
"\r\n" +
"-----1234\r\n" +
"Content-Disposition: form-data; name=\"normalFormField2\"\r\n" +
"\r\n" +
"long string should not work" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = container.inject(MyFileupAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext()
.withParameters(HttpParameters.create(param).build())
.withServletRequest(createMultipartRequestMaxStringLength());
interceptor.intercept(mai);
assertTrue(action.hasActionErrors());
Collection<String> errors = action.getActionErrors();
assertEquals(1, errors.size());
String msg = errors.iterator().next();
assertEquals(
"The request parameter \"normalFormField2\" was too long. Max length allowed is 20, but found 27!",
msg);
}
public void testMultipartRequestLocalizedError() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileupAction action = container.inject(MyFileupAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setAction(action);
mai.setResultCode("success");
mai.setInvocationContext(ActionContext.getContext());
Map<String, Object> param = new HashMap<>();
ActionContext.getContext()
.withParameters(HttpParameters.create(param).build())
.withLocale(Locale.GERMAN)
.withServletRequest(createMultipartRequestMaxSize(10));
interceptor.intercept(mai);
assertTrue(action.hasActionErrors());
Collection<String> errors = action.getActionErrors();
assertEquals(1, errors.size());
String msg = errors.iterator().next();
// the error message should contain at least this test
assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte Größe"));
}
public void testSkippingUploadedFileAware() throws Exception {
request.setCharacterEncoding(StandardCharsets.UTF_8.name());
request.setMethod("post");
request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
// inspired by the unit tests for jakarta commons fileupload
String content = ("-----1234\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"deleteme.txt\"\r\n" +
"Content-Type: text/html\r\n" +
"\r\n" +
"Unit test of FileUploadInterceptor" +
"\r\n" +
"-----1234--\r\n");
request.setContent(content.getBytes(StandardCharsets.US_ASCII));
MyFileUploadAction action = container.inject(MyFileUploadAction.class);
MockActionInvocation mai = new MockActionInvocation();
mai.setInvocationContext(ActionContext.getContext());
mai.setAction(action);
MockActionProxy map = new MockActionProxy();
map.setActionName("uploadedFiles");
mai.setProxy(map);
ActionContext.getContext()
.withParameters(HttpParameters.create(new HashMap<String, Object>()).build())
.withServletRequest(createMultipartRequestMaxSize(10));
interceptor.intercept(mai);
assertFalse(action.hasActionErrors());
}
private String encodeTextFile(String filename, String contentType, String content) {
return "\r\n" +
"--" +
"simple boundary" +
"\r\n" +
"Content-Disposition: form-data; name=\"" +
"file" +
"\"; filename=\"" +
filename +
"\r\n" +
"Content-Type: " +
contentType +
"\r\n" +
"\r\n" +
content;
}
private MultiPartRequestWrapper createMultipartRequestMaxFileSize() {
return createMultipartRequest(-1, 10, -1, -1);
}
private MultiPartRequestWrapper createMultipartRequestMaxFiles() {
return createMultipartRequest(-1, -1, 3, -1);
}
private MultiPartRequestWrapper createMultipartRequestMaxSize(int maxsize) {
return createMultipartRequest(maxsize, -1, -1, -1);
}
private MultiPartRequestWrapper createMultipartRequestMaxStringLength() {
return createMultipartRequest(-1, -1, -1, 20);
}
private MultiPartRequestWrapper createMultipartRequest(int maxsize, int maxfilesize, int maxfiles, int maxStringLength) {
JakartaMultiPartRequest jak = new JakartaMultiPartRequest();
jak.setMaxSize(String.valueOf(maxsize));
jak.setMaxFileSize(String.valueOf(maxfilesize));
jak.setMaxFiles(String.valueOf(maxfiles));
jak.setMaxStringLength(String.valueOf(maxStringLength));
jak.setDefaultEncoding(StandardCharsets.UTF_8.name());
return new MultiPartRequestWrapper(jak, request, tempDir.getAbsolutePath(), new DefaultLocaleProvider());
}
@Override
protected void setUp() throws Exception {
super.setUp();
request = new MockHttpServletRequest();
interceptor = new FileUploadInterceptor();
container.inject(interceptor);
tempDir = File.createTempFile("struts", "fileupload");
assertThat(tempDir.delete()).isTrue();
assertThat(tempDir.mkdirs()).isTrue();
}
@Override
protected void tearDown() throws Exception {
interceptor.destroy();
super.tearDown();
}
public static class MyFileupAction extends ActionSupport {
}
public static class MyFileUploadAction extends ActionSupport implements UploadedFilesAware {
private List<UploadedFile> uploadedFiles;
@Override
public void withUploadedFiles(List<UploadedFile> uploadedFiles) {
this.uploadedFiles = uploadedFiles;
}
public List<UploadedFile> getUploadFiles() {
return this.uploadedFiles;
}
}
}
@@ -47,7 +47,7 @@
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="actionFileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
@@ -86,7 +86,7 @@
<interceptor-ref name="modelDriven">
<param name="refreshModelBeforeResult">true</param>
</interceptor-ref>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="actionFileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params"/>