Skip to content
Snippets Groups Projects
Commit 5e0401fa authored by srosse's avatar srosse
Browse files

OO-4079: limit the depth of recursion to create folders or upload files

parent 01a900dd
No related branches found
No related tags found
No related merge requests found
/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.core.util.vfs.restapi;
import java.io.IOException;
/**
*
*
* Initial date: 6 juin 2019<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class VFSDepthException extends IOException {
private static final long serialVersionUID = 7550064282144460698L;
}
...@@ -74,8 +74,9 @@ public class VFSWebservice { ...@@ -74,8 +74,9 @@ public class VFSWebservice {
private static final String VERSION = "1.0"; private static final String VERSION = "1.0";
private static final OLog log = Tracing.createLoggerFor(VFSWebservice.class); private static final OLog log = Tracing.createLoggerFor(VFSWebservice.class);
private static final int MAX_FOLDER_DEPTH = 20;
public static CacheControl cc = new CacheControl(); private static final CacheControl cc = new CacheControl();
static { static {
cc.setMaxAge(-1); cc.setMaxAge(-1);
} }
...@@ -182,8 +183,13 @@ public class VFSWebservice { ...@@ -182,8 +183,13 @@ public class VFSWebservice {
public Response postFile64ToRoot(@FormParam("foldername") String foldername, @FormParam("filename") String filename, public Response postFile64ToRoot(@FormParam("foldername") String foldername, @FormParam("filename") String filename,
@FormParam("file") String file, @Context UriInfo uriInfo) { @FormParam("file") String file, @Context UriInfo uriInfo) {
byte[] fileAsBytes = Base64.decodeBase64(file); byte[] fileAsBytes = Base64.decodeBase64(file);
InputStream in = new ByteArrayInputStream(fileAsBytes); try(InputStream in = new ByteArrayInputStream(fileAsBytes)) {
return putFile(foldername, filename, in, uriInfo, Collections.<PathSegment>emptyList()); return putFile(foldername, filename, in, uriInfo, Collections.<PathSegment>emptyList());
} catch (VFSDepthException e) {
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
} catch (IOException e) {
return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
}
} }
/** /**
...@@ -224,8 +230,13 @@ public class VFSWebservice { ...@@ -224,8 +230,13 @@ public class VFSWebservice {
public Response postFile64ToFolder(@FormParam("foldername") String foldername, @FormParam("filename") String filename, public Response postFile64ToFolder(@FormParam("foldername") String foldername, @FormParam("filename") String filename,
@FormParam("file") String file, @Context UriInfo uriInfo, @PathParam("path") List<PathSegment> path) { @FormParam("file") String file, @Context UriInfo uriInfo, @PathParam("path") List<PathSegment> path) {
byte[] fileAsBytes = Base64.decodeBase64(file); byte[] fileAsBytes = Base64.decodeBase64(file);
InputStream in = new ByteArrayInputStream(fileAsBytes); try(InputStream in = new ByteArrayInputStream(fileAsBytes)) {
return putFile(foldername, filename, in, uriInfo, path); return putFile(foldername, filename, in, uriInfo, path);
} catch (VFSDepthException e) {
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
} catch (IOException e) {
return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
}
} }
/** /**
...@@ -260,8 +271,13 @@ public class VFSWebservice { ...@@ -260,8 +271,13 @@ public class VFSWebservice {
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response putFile64VOToRoot(File64VO file, @Context UriInfo uriInfo) { public Response putFile64VOToRoot(File64VO file, @Context UriInfo uriInfo) {
byte[] fileAsBytes = Base64.decodeBase64(file.getFile()); byte[] fileAsBytes = Base64.decodeBase64(file.getFile());
InputStream in = new ByteArrayInputStream(fileAsBytes); try(InputStream in = new ByteArrayInputStream(fileAsBytes)) {
return putFile(null, file.getFilename(), in, uriInfo, Collections.<PathSegment>emptyList()); return putFile(null, file.getFilename(), in, uriInfo, Collections.<PathSegment>emptyList());
} catch (VFSDepthException e) {
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
} catch (IOException e) {
return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
}
} }
/** /**
...@@ -300,6 +316,9 @@ public class VFSWebservice { ...@@ -300,6 +316,9 @@ public class VFSWebservice {
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
log.error("", e); log.error("", e);
return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
} catch (VFSDepthException e) {
log.error("", e);
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
} finally { } finally {
MultipartReader.closeQuietly(partsReader); MultipartReader.closeQuietly(partsReader);
IOUtils.closeQuietly(in); IOUtils.closeQuietly(in);
...@@ -323,8 +342,13 @@ public class VFSWebservice { ...@@ -323,8 +342,13 @@ public class VFSWebservice {
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response putFile64ToFolder(File64VO file, @Context UriInfo uriInfo, @PathParam("path") List<PathSegment> path) { public Response putFile64ToFolder(File64VO file, @Context UriInfo uriInfo, @PathParam("path") List<PathSegment> path) {
byte[] fileAsBytes = Base64.decodeBase64(file.getFile()); byte[] fileAsBytes = Base64.decodeBase64(file.getFile());
InputStream in = new ByteArrayInputStream(fileAsBytes); try(InputStream in = new ByteArrayInputStream(fileAsBytes)) {
return putFile(null, file.getFilename(), in, uriInfo, path); return putFile(null, file.getFilename(), in, uriInfo, path);
} catch (VFSDepthException e) {
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
} catch(IOException e) {
return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build();
}
} }
/** /**
...@@ -368,15 +392,23 @@ public class VFSWebservice { ...@@ -368,15 +392,23 @@ public class VFSWebservice {
if(container.getLocalSecurityCallback() != null && !container.getLocalSecurityCallback().canWrite()) { if(container.getLocalSecurityCallback() != null && !container.getLocalSecurityCallback().canWrite()) {
return Response.serverError().status(Status.UNAUTHORIZED).build(); return Response.serverError().status(Status.UNAUTHORIZED).build();
} }
if(path.size() >= MAX_FOLDER_DEPTH) {
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
}
VFSContainer directory = resolveContainer(path, true); try {
if(directory == null) { VFSContainer directory = resolveContainer(path, true);
return Response.serverError().status(Status.NOT_FOUND).build(); if(directory == null) {
return Response.serverError().status(Status.NOT_FOUND).build();
}
return Response.ok(createFileVO(directory, uriInfo)).build();
} catch (VFSDepthException e) {
return Response.serverError().status(Status.NOT_ACCEPTABLE).build();
} }
return Response.ok(createFileVO(directory, uriInfo)).build();
} }
protected Response putFile(String foldername, String filename, InputStream file, UriInfo uriInfo, List<PathSegment> path) { private Response putFile(String foldername, String filename, InputStream file, UriInfo uriInfo, List<PathSegment> path)
throws VFSDepthException {
if(container.getLocalSecurityCallback() != null && !container.getLocalSecurityCallback().canWrite()) { if(container.getLocalSecurityCallback() != null && !container.getLocalSecurityCallback().canWrite()) {
return Response.serverError().status(Status.UNAUTHORIZED).build(); return Response.serverError().status(Status.UNAUTHORIZED).build();
} }
...@@ -476,15 +508,19 @@ public class VFSWebservice { ...@@ -476,15 +508,19 @@ public class VFSWebservice {
return Response.serverError().status(Status.BAD_REQUEST).build(); return Response.serverError().status(Status.BAD_REQUEST).build();
} }
protected VFSContainer resolveContainer(List<PathSegment> path, boolean create) { protected VFSContainer resolveContainer(List<PathSegment> path, boolean create) throws VFSDepthException {
VFSContainer directory = container; VFSContainer directory = container;
boolean notFound = false; boolean notFound = false;
//remove trailing segment if a trailing / is used //remove trailing segment if a trailing / is used
if(path.size() > 0 && !StringHelper.containsNonWhitespace(path.get(path.size() - 1).getPath())) { if(!path.isEmpty() && !StringHelper.containsNonWhitespace(path.get(path.size() - 1).getPath())) {
path = path.subList(0, path.size() -1); path = path.subList(0, path.size() -1);
} }
if(create && path.size() >= MAX_FOLDER_DEPTH) {
throw new VFSDepthException();
}
a_a: a_a:
for(PathSegment seg:path) { for(PathSegment seg:path) {
String segPath = seg.getPath(); String segPath = seg.getPath();
...@@ -517,7 +553,7 @@ public class VFSWebservice { ...@@ -517,7 +553,7 @@ public class VFSWebservice {
boolean notFound = false; boolean notFound = false;
//remove trailing segment if a trailing / is used //remove trailing segment if a trailing / is used
if(path.size() > 0 && !StringHelper.containsNonWhitespace(path.get(path.size() - 1).getPath())) { if(!path.isEmpty() && !StringHelper.containsNonWhitespace(path.get(path.size() - 1).getPath())) {
path = path.subList(0, path.size() -1); path = path.subList(0, path.size() -1);
} }
......
...@@ -219,6 +219,20 @@ public class CoursesFoldersTest extends OlatJerseyTestCase { ...@@ -219,6 +219,20 @@ public class CoursesFoldersTest extends OlatJerseyTestCase {
assertTrue(item2 instanceof VFSContainer); assertTrue(item2 instanceof VFSContainer);
} }
@Test
public void testCreateFolders_tooMany() throws IOException, URISyntaxException {
assertTrue(conn.login("administrator", "openolat"));
URI uri = UriBuilder.fromUri(getNodeURI()).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")
.path("Folder").path("Folder").path("Folder").path("Folder").path("Folder").build();
HttpPut method = conn.createPut(uri, MediaType.APPLICATION_JSON, true);
HttpResponse response = conn.execute(method);
assertEquals(406, response.getStatusLine().getStatusCode());
}
@Test @Test
public void deleteFolder() throws IOException, URISyntaxException { public void deleteFolder() throws IOException, URISyntaxException {
//add some folders //add some folders
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment