diff --git a/tests/tas-restapi/src/test/java/org/alfresco/rest/servlet/DownloadContentServletTests.java b/tests/tas-restapi/src/test/java/org/alfresco/rest/servlet/DownloadContentServletTests.java
new file mode 100644
index 0000000000..d562dc2d78
--- /dev/null
+++ b/tests/tas-restapi/src/test/java/org/alfresco/rest/servlet/DownloadContentServletTests.java
@@ -0,0 +1,324 @@
+package org.alfresco.rest.servlet;
+
+import io.restassured.RestAssured;
+import org.alfresco.rest.RestTest;
+import org.alfresco.rest.core.RestRequest;
+import org.alfresco.rest.core.RestResponse;
+import org.alfresco.utility.model.FileModel;
+import org.alfresco.utility.model.FileType;
+import org.alfresco.utility.model.FolderModel;
+import org.alfresco.utility.model.SiteModel;
+import org.alfresco.utility.model.TestGroup;
+import org.alfresco.utility.model.UserModel;
+import org.alfresco.utility.report.Bug;
+import org.alfresco.utility.testrail.ExecutionType;
+import org.alfresco.utility.testrail.annotation.TestRail;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+public class DownloadContentServletTests extends RestTest
+{
+ private static final String CONTENT_DISPOSITION = "Content-Disposition";
+ private static final String FILENAME_HEADER = "filename=\"%s\"";
+ private static final String ATTACHMENT = "attachment";
+ private static final String FILE_CONTENT = "The content of the file.";
+
+ private static String downloadContentServletAttach = "alfresco/d/a/workspace/SpacesStore/";
+ private static String downloadContentServletDirect = "alfresco/d/d/workspace/SpacesStore/";
+
+ private UserModel testUser;
+ private FileModel testContentFile;
+ private String authHeaderEncoded;
+
+ @BeforeClass(alwaysRun = true)
+ public void dataPreparation() throws Exception
+ {
+ testUser = dataUser.createRandomTestUser();
+ SiteModel testSite = dataSite.usingUser(testUser).createPublicRandomSite();
+ FolderModel testFolder = dataContent.usingUser(testUser).usingSite(testSite).createFolder();
+ testContentFile = dataContent.usingUser(testUser).usingResource(testFolder)
+ .createContent(new FileModel("hotOuside", FileType.TEXT_PLAIN, FILE_CONTENT));
+
+ String authHeader = String.format("%s:%s", testUser.getUsername(), testUser.getPassword());
+ authHeaderEncoded = new String(Base64.encodeBase64(authHeader.getBytes()));
+
+ RestAssured.basePath = "";
+ restClient.configureRequestSpec().setBasePath(RestAssured.basePath);
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using short descriptor and attach short descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSShortAttachShort()
+ {
+ authenticateTestUser();
+ RestRequest request = RestRequest.simpleRequest(
+ HttpMethod.GET, downloadContentServletAttach + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using short descriptor and attach long descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSShortAttachLong()
+ {
+ authenticateTestUser();
+ String downloadContentServletAttachLong = "alfresco/d/attach/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentServletAttachLong + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using short descriptor and direct short descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSShortDirectShort()
+ {
+ authenticateTestUser();
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentServletDirect + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using short descriptor and direct long descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSShortDirectLong()
+ {
+ authenticateTestUser();
+ String downloadContentServletDirectLong = "alfresco/d/direct/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentServletDirectLong + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using long descriptor and attach short descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSLongAttachShort()
+ {
+ authenticateTestUser();
+ String downloadContentLongServletAttach = "alfresco/download/a/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLongServletAttach + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using long descriptor and attach long descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSLongAttachLong()
+ {
+ authenticateTestUser();
+ String downloadContentLongServletAttachLong = "alfresco/download/attach/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLongServletAttachLong + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using long descriptor and direct short descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSLongDirectShort()
+ {
+ authenticateTestUser();
+ String downloadContentLongServletDirect = "alfresco/download/d/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLongServletDirect + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using long descriptor and direct long descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSLongDirectLong()
+ {
+ authenticateTestUser();
+ String downloadContentLongServletDirectLong = "alfresco/download/direct/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLongServletDirectLong + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using short descriptor and attach short uppercase descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSShortAttachUppercaseShort()
+ {
+ authenticateTestUser();
+ String downloadContentAttachUppercase = "alfresco/d/A/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentAttachUppercase + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using short descriptor and direct short uppercase descriptor.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSShortDirectUppercaseShort()
+ {
+ authenticateTestUser();
+ String downloadContentDirectUppercase = "alfresco/d/D/workspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentDirectUppercase + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ RestResponse response = restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.OK);
+ assertEquals(FILE_CONTENT, response.getResponse().body().asString());
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, ATTACHMENT);
+ restClient.assertHeaderValueContains(CONTENT_DISPOSITION, String.format(FILENAME_HEADER, testContentFile.getName()));
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using attach without specifying {storeType}.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSAttachWithoutStoreType()
+ {
+ authenticateTestUser();
+ String downloadContentLessPathAttach = "alfresco/d/a/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLessPathAttach + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using direct without specifying {storeType}.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSDirectWithoutStoreType()
+ {
+ authenticateTestUser();
+ String downloadContentLessPathDirect = "alfresco/d/d/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLessPathDirect + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using direct without specifying {storeType}.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSDirectWithInvalidStoreType()
+ {
+ authenticateTestUser();
+ String downloadContentLessPathDirect = "alfresco/download/d/badWorkspace/SpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLessPathDirect + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using direct without specifying {storeType}.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSDirectWithInvalidStoreId()
+ {
+ authenticateTestUser();
+ String downloadContentLessPathDirect = "alfresco/download/d/workspace/badSpacesStore/";
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentLessPathDirect + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using attach without authentication.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSAttachWithoutAuthentication()
+ {
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentServletAttach + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.UNAUTHORIZED);
+ }
+
+ @TestRail(section = { TestGroup.REST_API },
+ executionType = ExecutionType.REGRESSION,
+ description = "Verify DownloadContentServlet retrieve content using direct without authentication.")
+ @Test(groups = { TestGroup.REST_API, TestGroup.FULL, TestGroup.ENTERPRISE})
+ @Bug(id ="MNT-21602", status=Bug.Status.FIXED)
+ public void verifyDCSDirectWithoutAuthentication()
+ {
+ RestRequest request = RestRequest.simpleRequest(HttpMethod.GET,
+ downloadContentServletDirect + testContentFile.getNodeRef() + "/" + testContentFile.getName());
+ restClient.process(request);
+ restClient.assertStatusCodeIs(HttpStatus.UNAUTHORIZED);
+ }
+
+ private void authenticateTestUser()
+ {
+ restClient.configureRequestSpec()
+ .addHeader("Authorization", String.format("Basic %s", authHeaderEncoded))
+ .build();
+ }
+}
diff --git a/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml b/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml
index 2d1bf24ff0..ff2ffaed47 100644
--- a/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml
+++ b/tests/tas-restapi/src/test/resources/test-suites/part1-suite.xml
@@ -14,6 +14,7 @@
+ * The URL to the servlet should be generated thus: + *
/alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf+ * or + *
/alfresco/download/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf+ *
+ * The 'attach' or 'direct' element is used to indicate whether to display the stream directly + * in the browser or download it as a file attachment.
+ *
+ * Since ACS 6.X, this Servlet redirects to GET /nodes/{nodeId}/content V1 REST API.
+ *
+ *
+ * @author Kevin Roast
+ * @author gavinc
+ */
+public class DownloadContentServlet extends HttpServlet
+{
+ private static final long serialVersionUID = -576405943603122206L;
+
+ private static Log logger = LogFactory.getLog(DownloadContentServlet.class);
+
+ private static final String URL_ATTACH = "a";
+ private static final String URL_ATTACH_LONG = "attach";
+ private static final String URL_DIRECT = "d";
+ private static final String URL_DIRECT_LONG = "direct";
+
+ /**
+ * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
+ */
+ protected void doGet(final HttpServletRequest req, final HttpServletResponse res)
+ throws IOException
+ {
+ if (logger.isDebugEnabled())
+ {
+ String queryString = req.getQueryString();
+ logger.debug("Authenticating (GET) request to URL: " + req.getRequestURI() +
+ ((queryString != null && queryString.length() > 0) ? ("?" + queryString) : ""));
+ }
+
+ // remove request context.
+ String requestURI = req.getRequestURI();
+ requestURI = requestURI.substring(req.getContextPath().length());
+
+ StringTokenizer t = new StringTokenizer(requestURI, "/");
+ int tokenCount = t.countTokens();
+ t.nextToken(); // skip servlet name
+
+ // expect a minimum of 6 URL tokens.
+ // /d/{attach|direct}/{storeType}/{storeId}/{nodeId}/{nodeName}
+ if(tokenCount < 6)
+ {
+ throw new IllegalArgumentException("Download URL did not contain all required args: " + requestURI);
+ }
+
+ // attachment mode (either 'attach' or 'direct')
+ String attachToken = t.nextToken();
+ boolean isAttachment = URL_ATTACH.equalsIgnoreCase(attachToken) || URL_ATTACH_LONG.equalsIgnoreCase(attachToken);
+ boolean isDirect = URL_DIRECT.equalsIgnoreCase(attachToken) || URL_DIRECT_LONG.equalsIgnoreCase(attachToken);
+ if (!(isAttachment || isDirect))
+ {
+ throw new IllegalArgumentException("Attachment mode is not properly specified: " + requestURI);
+ }
+
+ // allow only nodes from workspace://SpaceStore/ storeRef
+ StoreRef storeRef = new StoreRef(URLDecoder.decode(t.nextToken()), URLDecoder.decode(t.nextToken()));
+ boolean isWorkspaceStoreType = storeRef.getProtocol().equalsIgnoreCase("workspace");
+ boolean isSpacesStoreStoreId = storeRef.getIdentifier().equalsIgnoreCase("SpacesStore");
+
+ if (!isWorkspaceStoreType || !isSpacesStoreStoreId)
+ {
+ throw new IllegalArgumentException("Servlet accepts only nodes from workspace://SpaceStore/ storeRef: " + requestURI);
+ }
+
+ String nodeId = URLDecoder.decode(t.nextToken());
+
+ // build redirect URL to V1 GET /nodes/{nodeId}/content
+ String redirectUrl = String
+ .format("%s/api/-default-/public/alfresco/versions/1/nodes/%s/content?attachment=%b",
+ req.getContextPath(), nodeId, isAttachment);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Request redirected to URL: " + redirectUrl);
+ }
+ res.sendRedirect(redirectUrl);
+ }
+}
diff --git a/war/src/main/webapp/WEB-INF/web.xml b/war/src/main/webapp/WEB-INF/web.xml
index 616726cea8..9c102703d9 100644
--- a/war/src/main/webapp/WEB-INF/web.xml
+++ b/war/src/main/webapp/WEB-INF/web.xml
@@ -243,6 +243,10 @@
+