mirror of
https://github.com/sismics/docs.git
synced 2025-12-13 09:46:17 +00:00
#41: Quota increase/decrease when file is added/delete
+ java.nio-ization
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -165,13 +168,16 @@ public class AppResource extends BaseResource {
|
||||
}
|
||||
|
||||
// Check if each stored file is valid
|
||||
java.io.File[] storedFileList = DirectoryUtil.getStorageDirectory().listFiles();
|
||||
for (java.io.File storedFile : storedFileList) {
|
||||
String fileName = storedFile.getName();
|
||||
String[] fileNameArray = fileName.split("_");
|
||||
if (!fileMap.containsKey(fileNameArray[0])) {
|
||||
storedFile.delete();
|
||||
try (DirectoryStream<java.nio.file.Path> storedFileList = Files.newDirectoryStream(DirectoryUtil.getStorageDirectory())) {
|
||||
for (java.nio.file.Path storedFile : storedFileList) {
|
||||
String fileName = storedFile.getFileName().toString();
|
||||
String[] fileNameArray = fileName.split("_");
|
||||
if (!fileMap.containsKey(fileNameArray[0])) {
|
||||
Files.delete(storedFile);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ServerException("FileError", "Error deleting orphan files", e);
|
||||
}
|
||||
|
||||
// Always return OK
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.sismics.docs.rest.resource;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -123,6 +123,11 @@ public class FileResource extends BaseResource {
|
||||
throw new ClientException("InvalidFileType", "File type not recognized");
|
||||
}
|
||||
|
||||
// Validate quota
|
||||
if (user.getStorageCurrent() + fileData.length > user.getStorageQuota()) {
|
||||
throw new ClientException("QuotaReached", "Quota limit reached");
|
||||
}
|
||||
|
||||
try {
|
||||
// Get files of this document
|
||||
FileDao fileDao = new FileDao();
|
||||
@@ -144,6 +149,10 @@ public class FileResource extends BaseResource {
|
||||
// Save the file
|
||||
FileUtil.save(fileInputStream, file, user.getPrivateKey());
|
||||
|
||||
// Update the user quota
|
||||
user.setStorageCurrent(user.getStorageCurrent() + fileData.length);
|
||||
userDao.update(user);
|
||||
|
||||
// Raise a new file created event if we have a document
|
||||
if (documentId != null) {
|
||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||
@@ -206,8 +215,8 @@ public class FileResource extends BaseResource {
|
||||
|
||||
// Raise a new file created event (it wasn't sent during file creation)
|
||||
try {
|
||||
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), id).toFile();
|
||||
InputStream fileInputStream = new FileInputStream(storedfile);
|
||||
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(id);
|
||||
InputStream fileInputStream = Files.newInputStream(storedFile);
|
||||
final InputStream responseInputStream = EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey());
|
||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||
fileCreatedAsyncEvent.setDocument(document);
|
||||
@@ -341,6 +350,17 @@ public class FileResource extends BaseResource {
|
||||
// Delete the file
|
||||
fileDao.delete(file.getId());
|
||||
|
||||
// Update the user quota
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getById(principal.getId());
|
||||
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(id);
|
||||
try {
|
||||
user.setStorageCurrent(user.getStorageCurrent() - Files.size(storedFile));
|
||||
userDao.update(user);
|
||||
} catch (IOException e) {
|
||||
// The file doesn't exists on disk, which is weird, but not fatal
|
||||
}
|
||||
|
||||
// Raise a new file deleted event
|
||||
FileDeletedAsyncEvent fileDeletedAsyncEvent = new FileDeletedAsyncEvent();
|
||||
fileDeletedAsyncEvent.setFile(file);
|
||||
@@ -396,30 +416,33 @@ public class FileResource extends BaseResource {
|
||||
|
||||
|
||||
// Get the stored file
|
||||
java.io.File storedfile;
|
||||
java.nio.file.Path storedFile;
|
||||
String mimeType;
|
||||
boolean decrypt = false;
|
||||
if (size != null) {
|
||||
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId + "_" + size).toFile();
|
||||
storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId + "_" + size);
|
||||
mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG
|
||||
decrypt = true; // Thumbnails are encrypted
|
||||
if (!storedfile.exists()) {
|
||||
storedfile = new java.io.File(getClass().getResource("/image/file.png").getFile());
|
||||
if (!Files.exists(storedFile)) {
|
||||
storedFile = Paths.get(getClass().getResource("/image/file.png").getFile());
|
||||
mimeType = MimeType.IMAGE_PNG;
|
||||
decrypt = false;
|
||||
}
|
||||
} else {
|
||||
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId).toFile();
|
||||
storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId);
|
||||
mimeType = file.getMimeType();
|
||||
decrypt = true; // Original files are encrypted
|
||||
}
|
||||
|
||||
// Stream the output and decrypt it if necessary
|
||||
StreamingOutput stream;
|
||||
|
||||
// A file is always encrypted by the creator of it
|
||||
User user = userDao.getById(file.getUserId());
|
||||
|
||||
// Write the decrypted file to the output
|
||||
try {
|
||||
InputStream fileInputStream = new FileInputStream(storedfile);
|
||||
InputStream fileInputStream = Files.newInputStream(storedFile);
|
||||
final InputStream responseInputStream = decrypt ?
|
||||
EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()) : fileInputStream;
|
||||
|
||||
@@ -484,8 +507,8 @@ public class FileResource extends BaseResource {
|
||||
// Add each file to the ZIP stream
|
||||
int index = 0;
|
||||
for (File file : fileList) {
|
||||
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
|
||||
InputStream fileInputStream = new FileInputStream(storedfile);
|
||||
java.nio.file.Path storedfile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
|
||||
InputStream fileInputStream = Files.newInputStream(storedfile);
|
||||
|
||||
// Add the decrypted file to the ZIP stream
|
||||
// Files are encrypted by the creator of them
|
||||
|
||||
@@ -489,6 +489,8 @@ public class UserResource extends BaseResource {
|
||||
.add("id", userDto.getId())
|
||||
.add("username", userDto.getUsername())
|
||||
.add("email", userDto.getEmail())
|
||||
.add("storage_quota", userDto.getStorageQuota())
|
||||
.add("storage_current", userDto.getStorageCurrent())
|
||||
.add("create_date", userDto.getCreateTimestamp()));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.json.JsonArray;
|
||||
@@ -237,9 +236,9 @@ public class TestDocumentResource extends BaseJerseyTest {
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Check that the associated files are deleted from FS
|
||||
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
||||
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile();
|
||||
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile();
|
||||
java.io.File storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id).toFile();
|
||||
java.io.File webFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_web").toFile();
|
||||
java.io.File thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_thumb").toFile();
|
||||
Assert.assertFalse(storedFile.exists());
|
||||
Assert.assertFalse(webFile.exists());
|
||||
Assert.assertFalse(thumbnailFile.exists());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.sismics.docs.rest;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.json.JsonArray;
|
||||
@@ -121,8 +121,8 @@ public class TestFileResource extends BaseJerseyTest {
|
||||
Assert.assertTrue(fileBytes.length > 0);
|
||||
|
||||
// Check that the files are not readable directly from FS
|
||||
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
||||
try (InputStream storedFileInputStream = new BufferedInputStream(new FileInputStream(storedFile))) {
|
||||
Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id);
|
||||
try (InputStream storedFileInputStream = new BufferedInputStream(Files.newInputStream(storedFile))) {
|
||||
Assert.assertNull(MimeTypeUtil.guessMimeType(storedFileInputStream));
|
||||
}
|
||||
|
||||
@@ -179,12 +179,12 @@ public class TestFileResource extends BaseJerseyTest {
|
||||
Assert.assertEquals(Status.NOT_FOUND, Status.fromStatusCode(response.getStatus()));
|
||||
|
||||
// Check that files are deleted from FS
|
||||
storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
||||
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile();
|
||||
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile();
|
||||
Assert.assertFalse(storedFile.exists());
|
||||
Assert.assertFalse(webFile.exists());
|
||||
Assert.assertFalse(thumbnailFile.exists());
|
||||
storedFile = DirectoryUtil.getStorageDirectory().resolve(file1Id);
|
||||
Path webFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_web");
|
||||
Path thumbnailFile = DirectoryUtil.getStorageDirectory().resolve(file1Id + "_thumb");
|
||||
Assert.assertFalse(Files.exists(storedFile));
|
||||
Assert.assertFalse(Files.exists(webFile));
|
||||
Assert.assertFalse(Files.exists(thumbnailFile));
|
||||
|
||||
// Get all files from a document
|
||||
json = target().path("/file/list")
|
||||
@@ -198,7 +198,7 @@ public class TestFileResource extends BaseJerseyTest {
|
||||
|
||||
@Test
|
||||
public void testOrphanFile() throws Exception {
|
||||
// Login file1
|
||||
// Login file2
|
||||
clientUtil.createUser("file2");
|
||||
String file2AuthenticationToken = clientUtil.login("file2");
|
||||
|
||||
@@ -280,4 +280,97 @@ public class TestFileResource extends BaseJerseyTest {
|
||||
.delete(JsonObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuota() throws Exception {
|
||||
// Login file_quota
|
||||
clientUtil.createUser("file_quota");
|
||||
String fileQuotaAuthenticationToken = clientUtil.login("file_quota");
|
||||
|
||||
// Add a file (292641 bytes large)
|
||||
String file1Id = null;
|
||||
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||
JsonObject json = target()
|
||||
.register(MultiPartFeature.class)
|
||||
.path("/file").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||
file1Id = json.getString("id");
|
||||
Assert.assertNotNull(file1Id);
|
||||
}
|
||||
}
|
||||
|
||||
// Check current quota
|
||||
JsonObject json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertEquals(292641l, json.getJsonNumber("storage_current").longValue());
|
||||
|
||||
// Add a file (292641 bytes large)
|
||||
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||
target()
|
||||
.register(MultiPartFeature.class)
|
||||
.path("/file").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||
}
|
||||
}
|
||||
|
||||
// Check current quota
|
||||
json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue());
|
||||
|
||||
// Add a file (292641 bytes large)
|
||||
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||
target()
|
||||
.register(MultiPartFeature.class)
|
||||
.path("/file").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||
MediaType.MULTIPART_FORM_DATA_TYPE), JsonObject.class);
|
||||
}
|
||||
}
|
||||
|
||||
// Check current quota
|
||||
json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertEquals(877923l, json.getJsonNumber("storage_current").longValue());
|
||||
|
||||
// Add a file (292641 bytes large)
|
||||
try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) {
|
||||
StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png");
|
||||
try (FormDataMultiPart multiPart = new FormDataMultiPart()) {
|
||||
Response response = target()
|
||||
.register(MultiPartFeature.class)
|
||||
.path("/file").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.put(Entity.entity(multiPart.bodyPart(streamDataBodyPart),
|
||||
MediaType.MULTIPART_FORM_DATA_TYPE));
|
||||
Assert.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes a file
|
||||
json = target().path("/file/" + file1Id).request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.delete(JsonObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// Check current quota
|
||||
json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, fileQuotaAuthenticationToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertEquals(585282l, json.getJsonNumber("storage_current").longValue());
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,13 @@ public class TestUserResource extends BaseJerseyTest {
|
||||
.get(JsonObject.class);
|
||||
JsonArray users = json.getJsonArray("users");
|
||||
Assert.assertTrue(users.size() > 0);
|
||||
JsonObject user = users.getJsonObject(0);
|
||||
Assert.assertNotNull(user.getString("id"));
|
||||
Assert.assertNotNull(user.getString("username"));
|
||||
Assert.assertNotNull(user.getString("email"));
|
||||
Assert.assertNotNull(user.getJsonNumber("storage_quota"));
|
||||
Assert.assertNotNull(user.getJsonNumber("storage_current"));
|
||||
Assert.assertNotNull(user.getJsonNumber("create_date"));
|
||||
|
||||
// Create a user KO (login length validation)
|
||||
Response response = target().path("/user").request()
|
||||
|
||||
Reference in New Issue
Block a user