diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java index e8af4e83..6156a126 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/FileDao.java @@ -61,6 +61,21 @@ public class FileDao { return (File) q.getSingleResult(); } + /** + * Returns an active file. + * + * @param id File ID + * @param userId User ID + * @return Document + */ + public File getFile(String id, String userId) { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + Query q = em.createQuery("select f from File f where f.id = :id and f.userId = :userId and f.deleteDate is null"); + q.setParameter("id", id); + q.setParameter("userId", userId); + return (File) q.getSingleResult(); + } + /** * Deletes a file. * @@ -117,16 +132,18 @@ public class FileDao { } /** - * Get files by document ID. + * Get files by document ID or all orphan files of an user. * + * @parma userId User ID * @param documentId Document ID * @return List of files */ @SuppressWarnings("unchecked") - public List getByDocumentId(String documentId) { + public List getByDocumentId(String userId, String documentId) { EntityManager em = ThreadLocalContext.get().getEntityManager(); if (documentId == null) { - Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null order by f.createDate asc"); + Query q = em.createQuery("select f from File f where f.documentId is null and f.deleteDate is null and f.userId = :userId order by f.createDate asc"); + q.setParameter("userId", userId); return q.getResultList(); } Query q = em.createQuery("select f from File f where f.documentId = :documentId and f.deleteDate is null order by f.order asc"); diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java index d1d46f4e..5ddd6641 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/File.java @@ -27,9 +27,15 @@ public class File { /** * Document ID. */ - @Column(name = "FIL_IDDOC_C", nullable = false, length = 36) + @Column(name = "FIL_IDDOC_C", length = 36) private String documentId; + /** + * User ID. + */ + @Column(name = "FIL_IDUSER_C", length = 36) + private String userId; + /** * MIME type. */ @@ -186,6 +192,24 @@ public class File { public void setOrder(Integer order) { this.order = order; } + + /** + * Getter of userId. + * + * @return the userId + */ + public String getUserId() { + return userId; + } + + /** + * Setter of userId. + * + * @param userId userId + */ + public void setUserId(String userId) { + this.userId = userId; + } @Override public String toString() { diff --git a/docs-core/src/main/resources/db/update/dbupdate-007-0.sql b/docs-core/src/main/resources/db/update/dbupdate-007-0.sql index a1eba010..7660c00c 100644 --- a/docs-core/src/main/resources/db/update/dbupdate-007-0.sql +++ b/docs-core/src/main/resources/db/update/dbupdate-007-0.sql @@ -1,2 +1,3 @@ alter table T_FILE alter column FIL_IDDOC_C set null; +alter table T_FILE add column FIL_IDUSER_C varchar(36); update T_CONFIG set CFG_VALUE_C='7' where CFG_ID_C='DB_VERSION'; diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java index 2fc0a421..f8f7774c 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java @@ -471,7 +471,7 @@ public class DocumentResource extends BaseResource { List fileList; try { document = documentDao.getDocument(id, principal.getId()); - fileList = fileDao.getByDocumentId(id); + fileList = fileDao.getByDocumentId(principal.getId(), id); } catch (NoResultException e) { throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", id)); } diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java index 2700a7f2..3b5b7f6f 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java @@ -128,7 +128,7 @@ public class FileResource extends BaseResource { FileDao fileDao = new FileDao(); int order = 0; if (documentId != null) { - for (File file : fileDao.getByDocumentId(documentId)) { + for (File file : fileDao.getByDocumentId(principal.getId(), documentId)) { file.setOrder(order++); } } @@ -138,6 +138,7 @@ public class FileResource extends BaseResource { file.setOrder(order); file.setDocumentId(documentId); file.setMimeType(mimeType); + file.setUserId(principal.getId()); String fileId = fileDao.create(file); // Save the file @@ -192,7 +193,7 @@ public class FileResource extends BaseResource { Document document; File file; try { - file = fileDao.getFile(id); + file = fileDao.getFile(id, principal.getId()); document = documentDao.getDocument(documentId, principal.getId()); } catch (NoResultException e) { throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId)); @@ -205,7 +206,7 @@ public class FileResource extends BaseResource { // Update the file file.setDocumentId(documentId); - file.setOrder(fileDao.getByDocumentId(documentId).size()); + file.setOrder(fileDao.getByDocumentId(principal.getId(), documentId).size()); fileDao.update(file); // Raise a new file created event (it wasn't sent during file creation) @@ -260,7 +261,7 @@ public class FileResource extends BaseResource { // Reorder files FileDao fileDao = new FileDao(); - for (File file : fileDao.getByDocumentId(documentId)) { + for (File file : fileDao.getByDocumentId(principal.getId(), documentId)) { int order = idList.lastIndexOf(file.getId()); if (order != -1) { file.setOrder(order); @@ -274,9 +275,10 @@ public class FileResource extends BaseResource { } /** - * Returns files linked to a document. + * Returns files linked to a document or not linked to any document. * * @param documentId Document ID + * @param shareId Sharing ID * @return Response * @throws JSONException */ @@ -305,7 +307,7 @@ public class FileResource extends BaseResource { } FileDao fileDao = new FileDao(); - List fileList = fileDao.getByDocumentId(documentId); + List fileList = fileDao.getByDocumentId(principal.getId(), documentId); JSONObject response = new JSONObject(); List files = new ArrayList<>(); @@ -345,7 +347,15 @@ public class FileResource extends BaseResource { File file; try { file = fileDao.getFile(id); - documentDao.getDocument(file.getDocumentId(), principal.getId()); + if (file.getDocumentId() == null) { + // It's an orphan file + if (!file.getUserId().equals(principal.getId())) { + // But not ours + throw new ForbiddenClientException(); + } + } else { + documentDao.getDocument(file.getDocumentId(), principal.getId()); + } } catch (NoResultException e) { throw new ClientException("FileNotFound", MessageFormat.format("File not found: {0}", id)); } @@ -392,14 +402,28 @@ public class FileResource extends BaseResource { UserDao userDao = new UserDao(); File file; Document document; + String userId; try { file = fileDao.getFile(fileId); - document = documentDao.getDocument(file.getDocumentId()); - // Check document visibility - ShareDao shareDao = new ShareDao(); - if (!shareDao.checkVisibility(document, principal.getId(), shareId)) { - throw new ForbiddenClientException(); + if (file.getDocumentId() == null) { + // It's an orphan file + if (!file.getUserId().equals(principal.getId())) { + // But not ours + throw new ForbiddenClientException(); + } + + userId = file.getUserId(); + } else { + // It's a file linked to a document + document = documentDao.getDocument(file.getDocumentId()); + userId = document.getUserId(); + + // Check document visibility + ShareDao shareDao = new ShareDao(); + if (!shareDao.checkVisibility(document, principal.getId(), shareId)) { + throw new ForbiddenClientException(); + } } } catch (NoResultException e) { throw new ClientException("FileNotFound", MessageFormat.format("File not found: {0}", fileId)); @@ -427,7 +451,7 @@ public class FileResource extends BaseResource { // Stream the output and decrypt it if necessary StreamingOutput stream; - User user = userDao.getById(document.getUserId()); + User user = userDao.getById(userId); try { InputStream fileInputStream = new FileInputStream(storedfile); final InputStream responseInputStream = decrypt ? @@ -487,7 +511,7 @@ public class FileResource extends BaseResource { // Get files and user associated with this document FileDao fileDao = new FileDao(); UserDao userDao = new UserDao(); - final List fileList = fileDao.getByDocumentId(documentId); + final List fileList = fileDao.getByDocumentId(principal.getId(), documentId); final User user = userDao.getById(document.getUserId()); // Create the ZIP stream diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestFileResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestFileResource.java index 2db66c69..90f3155e 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestFileResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestFileResource.java @@ -228,6 +228,16 @@ public class TestFileResource extends BaseJerseyTest { JSONArray files = json.getJSONArray("files"); Assert.assertEquals(1, files.length()); + // Get the file data + fileResource = resource().path("/file/" + file1Id + "/data"); + fileResource.addFilter(new CookieAuthenticationFilter(file2AuthenticationToken)); + response = fileResource.get(ClientResponse.class); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + InputStream is = response.getEntityInputStream(); + byte[] fileBytes = ByteStreams.toByteArray(is); + Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes)); + Assert.assertEquals(163510, fileBytes.length); + // Create a document WebResource documentResource = resource().path("/document"); documentResource.addFilter(new CookieAuthenticationFilter(file2AuthenticationToken)); @@ -259,5 +269,27 @@ public class TestFileResource extends BaseJerseyTest { Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); files = json.getJSONArray("files"); Assert.assertEquals(1, files.length()); + + // Add a file + fileResource = resource().path("/file"); + fileResource.addFilter(new CookieAuthenticationFilter(file2AuthenticationToken)); + form = new FormDataMultiPart(); + file = this.getClass().getResourceAsStream("/file/PIA00452.jpg"); + fdp = new FormDataBodyPart("file", + new BufferedInputStream(file), + MediaType.APPLICATION_OCTET_STREAM_TYPE); + form.bodyPart(fdp); + response = fileResource.type(MediaType.MULTIPART_FORM_DATA).put(ClientResponse.class, form); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + json = response.getEntity(JSONObject.class); + String file2Id = json.getString("id"); + + // Deletes a file + fileResource = resource().path("/file/" + file2Id); + fileResource.addFilter(new CookieAuthenticationFilter(file2AuthenticationToken)); + response = fileResource.delete(ClientResponse.class); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + json = response.getEntity(JSONObject.class); + Assert.assertEquals("ok", json.getString("status")); } } \ No newline at end of file