diff --git a/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java b/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java index f87ecc274f19cf54798008d2444a0a7d9c4333ed..7fb7ca5668cdcecbdd1aec407ef94aba138de8c1 100644 --- a/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java +++ b/src/main/java/org/olat/restapi/security/RestApiLoginFilter.java @@ -20,6 +20,8 @@ package org.olat.restapi.security; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; @@ -103,7 +105,7 @@ public class RestApiLoginFilter implements Filter { HttpServletRequest httpRequest = (HttpServletRequest)request; HttpServletResponse httpResponse = (HttpServletResponse)response; - String requestURI = httpRequest.getRequestURI(); + String requestURI = getRequestURI(httpRequest); RestModule restModule = (RestModule)CoreSpringFactory.getBean("restModule"); if(restModule == null || !restModule.isEnabled() && !isRequestURIAlwaysEnabled(requestURI)) { httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); @@ -294,11 +296,11 @@ public class RestApiLoginFilter implements Filter { followToken(token, request, response, chain); return; } - //fxdiff FXOLAT-113: business path in DMZ + UserRequest ureq = null; try{ //upon creation URL is checked for - String requestURI = request.getRequestURI(); + String requestURI = getRequestURI(request); ureq = new UserRequestImpl(requestURI, request, response); } catch(NumberFormatException nfe) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); @@ -337,7 +339,7 @@ public class RestApiLoginFilter implements Filter { UserRequest ureq = null; try{ //upon creation URL is checked for - String requestURI = request.getRequestURI(); + String requestURI = getRequestURI(request); ureq = new UserRequestImpl(requestURI, request, response); ureq.getUserSession().putEntryInNonClearedStore(SYSTEM_MARKER, Boolean.TRUE); } catch(NumberFormatException nfe) { @@ -356,7 +358,7 @@ public class RestApiLoginFilter implements Filter { UserRequest ureq = null; try{ //upon creation URL is checked for - String requestURI = request.getRequestURI(); + String requestURI = getRequestURI(request); ureq = new UserRequestImpl(requestURI, request, response); } catch(Exception e) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); @@ -386,7 +388,7 @@ public class RestApiLoginFilter implements Filter { UserRequest ureq = null; try{ //upon creation URL is checked for - String requestURI = request.getRequestURI(); + String requestURI = getRequestURI(request); ureq = new UserRequestImpl(requestURI, request, response); } catch(NumberFormatException nfe) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); @@ -411,6 +413,18 @@ public class RestApiLoginFilter implements Filter { } return WebappHelper.getServletContextPath() != null; } + + private String getRequestURI(HttpServletRequest request) { + String requestURI = request.getRequestURI(); + if(StringHelper.containsNonWhitespace(requestURI)) { + try { + requestURI = URLDecoder.decode(requestURI, "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error("", e); + } + } + return requestURI; + } private String getLoginUrl() { if(LOGIN_URL == null && isWebappHelperInitiated()) { diff --git a/src/test/java/org/olat/restapi/CoursesFoldersTest.java b/src/test/java/org/olat/restapi/CoursesFoldersTest.java index 53b06f5ad4279eab5ad3adb73a0431a6648a37f1..7c902fa86b21b87d488b090102c08b611b601bb3 100644 --- a/src/test/java/org/olat/restapi/CoursesFoldersTest.java +++ b/src/test/java/org/olat/restapi/CoursesFoldersTest.java @@ -70,10 +70,6 @@ import org.springframework.beans.factory.annotation.Autowired; public class CoursesFoldersTest extends OlatJerseyTestCase { - private static ICourse course1; - private static CourseNode bcNode; - private static Identity admin, user; - private RestConnection conn; @Autowired @@ -84,23 +80,6 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { @Before public void setUp() throws Exception { conn = new RestConnection(); - - admin = securityManager.findIdentityByName("administrator"); - user = JunitTestHelper.createAndPersistIdentityAsUser("rest-cf-one"); - RepositoryEntry courseEntry = JunitTestHelper.deployBasicCourse(admin); - course1 = CourseFactory.loadCourse(courseEntry); - dbInstance.intermediateCommit(); - - //create a folder - CourseNodeConfiguration newNodeConfig = CourseNodeFactory.getInstance().getCourseNodeConfiguration("bc"); - bcNode = newNodeConfig.getInstance(); - bcNode.setShortTitle("Folder"); - bcNode.setLearningObjectives("Folder objectives"); - bcNode.setNoAccessExplanation("You don't have access"); - course1.getEditorTreeModel().addCourseNode(bcNode, course1.getRunStructure().getRootNode()); - - CourseFactory.publishCourse(course1, RepositoryEntryStatusEnum.published, true, false, admin, Locale.ENGLISH); - dbInstance.intermediateCommit(); } @@ -118,10 +97,10 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { @Test public void testGetFolderInfo() throws IOException, URISyntaxException { - boolean loggedIN = conn.login("administrator", "openolat"); - assertTrue(loggedIN); - - URI uri = UriBuilder.fromUri(getNodeURI()).build(); + assertTrue(conn.login("administrator", "openolat")); + + CourseWithBC courseWithBc = deployCourse(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).build(); HttpGet get = conn.createGet(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(get); assertEquals(200, response.getStatusLine().getStatusCode()); @@ -136,10 +115,11 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { */ @Test public void testGetFolderInfoByUser() throws IOException, URISyntaxException { - boolean loggedIN = conn.login(user.getName(), "A6B7C8"); - assertTrue(loggedIN); - - URI uri = UriBuilder.fromUri(getNodeURI()).build(); + Identity user = JunitTestHelper.createAndPersistIdentityAsRndUser("rest-user-bc"); + assertTrue(conn.login(user.getName(), "A6B7C8")); + + CourseWithBC courseWithBc = deployCourse(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).build(); HttpGet get = conn.createGet(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(get); assertEquals(200, response.getStatusLine().getStatusCode()); @@ -149,10 +129,10 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { @Test public void testGetFoldersInfo() throws IOException, URISyntaxException { - boolean loggedIN = conn.login("administrator", "openolat"); - assertTrue(loggedIN); + assertTrue(conn.login("administrator", "openolat")); + CourseWithBC courseWithBc = deployCourse(); - URI uri = UriBuilder.fromUri(getNodesURI()).build(); + URI uri = UriBuilder.fromUri(getNodesURI(courseWithBc)).build(); HttpGet get = conn.createGet(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(get); assertEquals(200, response.getStatusLine().getStatusCode()); @@ -166,8 +146,9 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { @Test public void testUploadFile() throws IOException, URISyntaxException { assertTrue(conn.login("administrator", "openolat")); + CourseWithBC courseWithBc = deployCourse(); - URI uri = UriBuilder.fromUri(getNodeURI()).path("files").build(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").build(); //create single page URL fileUrl = CoursesFoldersTest.class.getResource("singlepage.html"); @@ -179,21 +160,46 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); - VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)bcNode, course1.getCourseEnvironment()); + VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)courseWithBc.bcNode, courseWithBc.course.getCourseEnvironment()); VFSItem item = folder.resolve(file.getName()); assertNotNull(item); } + @Test + public void testUploadFile_withSpecialCharacter() throws IOException, URISyntaxException { + assertTrue(conn.login("administrator", "openolat")); + CourseWithBC courseWithBc = deployCourse(); + + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").build(); + + //create single page + URL fileUrl = CoursesFoldersTest.class.getResource("singlepage.html"); + assertNotNull(fileUrl); + File file = new File(fileUrl.toURI()); + String filename = "SingleP\u00E4ge.html"; + + HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); + conn.addMultipart(method, filename, file); + HttpResponse response = conn.execute(method); + assertEquals(200, response.getStatusLine().getStatusCode()); + + VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)courseWithBc.bcNode, courseWithBc.course.getCourseEnvironment()); + VFSItem item = folder.resolve(filename); + assertNotNull(item); + assertEquals(filename, item.getName()); + } + @Test public void testCreateFolder() throws IOException, URISyntaxException { assertTrue(conn.login("administrator", "openolat")); + CourseWithBC courseWithBc = deployCourse(); - URI uri = UriBuilder.fromUri(getNodeURI()).path("files").path("RootFolder").build(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").path("RootFolder").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); - VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)bcNode, course1.getCourseEnvironment()); + VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)courseWithBc.bcNode, courseWithBc.course.getCourseEnvironment()); VFSItem item = folder.resolve("RootFolder"); assertNotNull(item); assertTrue(item instanceof VFSContainer); @@ -202,13 +208,14 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { @Test public void testCreateFolders() throws IOException, URISyntaxException { assertTrue(conn.login("administrator", "openolat")); + CourseWithBC courseWithBc = deployCourse(); - URI uri = UriBuilder.fromUri(getNodeURI()).path("files").path("NewFolder1").path("NewFolder2").build(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").path("NewFolder1").path("NewFolder2").build(); HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); - VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)bcNode, course1.getCourseEnvironment()); + VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)courseWithBc.bcNode, courseWithBc.course.getCourseEnvironment()); VFSItem item = folder.resolve("NewFolder1"); assertNotNull(item); assertTrue(item instanceof VFSContainer); @@ -222,8 +229,8 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { @Test public void testCreateFolders_tooMany() throws IOException, URISyntaxException { assertTrue(conn.login("administrator", "openolat")); - - URI uri = UriBuilder.fromUri(getNodeURI()).path("files").path("RootFolder") + CourseWithBC courseWithBc = deployCourse(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").path("RootFolder") .path("Folder").path("Folder").path("Folder").path("Folder").path("Folder") .path("Folder").path("Folder").path("Folder").path("Folder").path("Folder") .path("Folder").path("Folder").path("Folder").path("Folder").path("Folder") @@ -233,10 +240,30 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { assertEquals(406, response.getStatusLine().getStatusCode()); } + @Test + public void testCreateFolders_withSpecialCharacters() throws IOException, URISyntaxException { + assertTrue(conn.login("administrator", "openolat")); + + CourseWithBC courseWithBc = deployCourse(); + + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").path("RootFolder") + .path("F\u00FClder").build(); + HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true); + HttpResponse response = conn.execute(method); + assertEquals(200, response.getStatusLine().getStatusCode()); + + VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)courseWithBc.bcNode, courseWithBc.course.getCourseEnvironment()); + VFSItem item = folder.resolve("RootFolder/F\u00FClder"); + assertNotNull(item); + assertTrue(item instanceof VFSContainer); + } + @Test public void deleteFolder() throws IOException, URISyntaxException { + CourseWithBC courseWithBc = deployCourse(); + //add some folders - VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)bcNode, course1.getCourseEnvironment()); + VFSContainer folder = BCCourseNode.getNodeFolderContainer((BCCourseNode)courseWithBc.bcNode, courseWithBc.course.getCourseEnvironment()); VFSItem item = folder.resolve("FolderToDelete"); if(item == null) { folder.createChildContainer("FolderToDelete"); @@ -244,7 +271,7 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { assertTrue(conn.login("administrator", "openolat")); - URI uri = UriBuilder.fromUri(getNodeURI()).path("files").path("FolderToDelete").build(); + URI uri = UriBuilder.fromUri(getNodeURI(courseWithBc)).path("files").path("FolderToDelete").build(); HttpDelete method = conn.createDelete(uri, MediaType.APPLICATION_JSON); HttpResponse response = conn.execute(method); assertEquals(200, response.getStatusLine().getStatusCode()); @@ -253,13 +280,42 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { assertNull(deletedItem); } - private URI getNodeURI() { - return UriBuilder.fromUri(getContextURI()).path("repo").path("courses").path(course1.getResourceableId().toString()) - .path("elements").path("folder").path(bcNode.getIdent()).build(); + private URI getNodeURI(CourseWithBC courseWithBc) { + return UriBuilder.fromUri(getContextURI()).path("repo").path("courses").path(courseWithBc.course.getResourceableId().toString()) + .path("elements").path("folder").path(courseWithBc.bcNode.getIdent()).build(); } - private URI getNodesURI() { - return UriBuilder.fromUri(getContextURI()).path("repo").path("courses").path(course1.getResourceableId().toString()) + private URI getNodesURI(CourseWithBC courseWithBc) { + return UriBuilder.fromUri(getContextURI()).path("repo").path("courses").path(courseWithBc.course.getResourceableId().toString()) .path("elements").path("folder").build(); } + + private CourseWithBC deployCourse() { + Identity admin = securityManager.findIdentityByName("administrator"); + RepositoryEntry courseEntry = JunitTestHelper.deployBasicCourse(admin); + ICourse course = CourseFactory.loadCourse(courseEntry); + dbInstance.intermediateCommit(); + + //create a folder + CourseNodeConfiguration newNodeConfig = CourseNodeFactory.getInstance().getCourseNodeConfiguration("bc"); + CourseNode bcNode = newNodeConfig.getInstance(); + bcNode.setShortTitle("Folder"); + bcNode.setLearningObjectives("Folder objectives"); + bcNode.setNoAccessExplanation("You don't have access"); + course.getEditorTreeModel().addCourseNode(bcNode, course.getRunStructure().getRootNode()); + + CourseFactory.publishCourse(course, RepositoryEntryStatusEnum.published, true, false, admin, Locale.ENGLISH); + return new CourseWithBC(course, bcNode); + } + + private static class CourseWithBC { + private final ICourse course; + private final CourseNode bcNode; + + public CourseWithBC(ICourse course, CourseNode bcNode) { + this.course = course; + this.bcNode = bcNode; + } + + } } diff --git a/src/test/java/org/olat/restapi/RestConnection.java b/src/test/java/org/olat/restapi/RestConnection.java index 64d9aff2488c9c9a22d4a5bf912251cc38ab96a1..2388de9dee3fd603612da192075dad299915e12a 100644 --- a/src/test/java/org/olat/restapi/RestConnection.java +++ b/src/test/java/org/olat/restapi/RestConnection.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -221,8 +222,8 @@ public class RestConnection { /** * Add an object (application/json) - * @param put - * @param obj + * @param put The request + * @param obj The object which will be serialized as json in UTF-8 * @throws UnsupportedEncodingException */ public void addJsonEntity(HttpEntityEnclosingRequestBase put, Object obj) @@ -234,12 +235,18 @@ public class RestConnection { put.setEntity(myEntity); } + /** + * @param post The request + * @param filename The filename (will encoded as UTF-8) + * @param file The file (application/octet-stream) + * @throws UnsupportedEncodingException + */ public void addMultipart(HttpEntityEnclosingRequestBase post, String filename, File file) throws UnsupportedEncodingException { HttpEntity entity = MultipartEntityBuilder.create() .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) - .addTextBody("filename", filename) + .addTextBody("filename", filename, ContentType.create("text/plain", StandardCharsets.UTF_8)) .addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, filename).build(); post.setEntity(entity); }