From fb75bafe96ef2a4e4c47327c55e820034dee16fd Mon Sep 17 00:00:00 2001 From: Benjamin Gamard Date: Mon, 20 Nov 2017 20:34:29 +0100 Subject: [PATCH] Closes #166: global quota --- .../sismics/docs/core/constant/Constants.java | 11 ++++-- .../sismics/docs/core/dao/jpa/UserDao.java | 39 ++++++++++--------- .../docs/rest/resource/AppResource.java | 16 +++++++- .../docs/rest/resource/FileResource.java | 24 ++++++++++-- .../controller/document/DocumentDefault.js | 2 +- docs-web/src/main/webapp/src/index.html | 4 ++ docs-web/src/main/webapp/src/locale/en.json | 3 +- .../sismics/docs/rest/TestAppResource.java | 1 + docs-web/src/test/resources/log4j.properties | 1 + 9 files changed, 71 insertions(+), 30 deletions(-) diff --git a/docs-core/src/main/java/com/sismics/docs/core/constant/Constants.java b/docs-core/src/main/java/com/sismics/docs/core/constant/Constants.java index 50cbcbd4..a9274e41 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/constant/Constants.java +++ b/docs-core/src/main/java/com/sismics/docs/core/constant/Constants.java @@ -46,23 +46,28 @@ public class Constants { public static final List SUPPORTED_LANGUAGES = Lists.newArrayList("eng", "fra", "ita", "deu", "spa", "por", "pol", "rus", "ukr", "ara", "hin", "chi_sim", "chi_tra", "jpn", "tha", "kor"); /** - * Base URL environnement variable. + * Base URL environment variable. */ public static final String BASE_URL_ENV = "DOCS_BASE_URL"; /** - * Default language environnement variable. + * Default language environment variable. */ public static final String DEFAULT_LANGUAGE_ENV = "DOCS_DEFAULT_LANGUAGE"; /** - * SMTP configuration environnement variables. + * SMTP configuration environment variables. */ public static final String SMTP_HOSTNAME_ENV = "DOCS_SMTP_HOSTNAME"; public static final String SMTP_PORT_ENV = "DOCS_SMTP_PORT"; public static final String SMTP_USERNAME_ENV = "DOCS_SMTP_USERNAME"; public static final String SMTP_PASSWORD_ENV = "DOCS_SMTP_PASSWORD"; + /** + * Global quota environment variable. + */ + public static final String GLOBAL_QUOTA_ENV = "DOCS_GLOBAL_QUOTA"; + /** * Expiration time of the password recovery in hours. */ diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java index 209fa97c..bd17e5b1 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java @@ -1,19 +1,5 @@ package com.sismics.docs.core.dao.jpa; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.Query; - -import org.mindrot.jbcrypt.BCrypt; - import com.google.common.base.Joiner; import com.sismics.docs.core.constant.AuditLogType; import com.sismics.docs.core.dao.jpa.criteria.UserCriteria; @@ -24,6 +10,13 @@ import com.sismics.docs.core.util.jpa.QueryParam; import com.sismics.docs.core.util.jpa.QueryUtil; import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.util.context.ThreadLocalContext; +import org.mindrot.jbcrypt.BCrypt; + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.Query; +import java.sql.Timestamp; +import java.util.*; /** * User DAO. @@ -59,7 +52,7 @@ public class UserDao { * @param user User to create * @param userId User ID * @return User ID - * @throws Exception + * @throws Exception e */ public String create(User user, String userId) throws Exception { // Create the user UUID @@ -116,9 +109,8 @@ public class UserDao { * Updates a user's quota. * * @param user User to update - * @return Updated user */ - public User updateQuota(User user) { + public void updateQuota(User user) { EntityManager em = ThreadLocalContext.get().getEntityManager(); // Get the user @@ -128,8 +120,6 @@ public class UserDao { // Update the user userFromDb.setStorageQuota(user.getStorageQuota()); - - return user; } /** @@ -298,4 +288,15 @@ public class UserDao { } return userDtoList; } + + /** + * Returns the global storage used by all users. + * + * @return Current global storage + */ + public long getGlobalStorageCurrent() { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + Query query = em.createNativeQuery("select sum(u.USE_STORAGECURRENT_N) from T_USER u where u.USE_DELETEDATE_D is null"); + return ((Number) query.getSingleResult()).longValue(); + } } diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java index e190fea9..ec4cbb15 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java @@ -63,6 +63,8 @@ public class AppResource extends BaseResource { * @apiSuccess {Boolean} guest_login True if guest login is enabled * @apiSuccess {String} total_memory Allocated JVM memory (in bytes) * @apiSuccess {String} free_memory Free JVM memory (in bytes) + * @apiSuccess {String} global_storage_current Global storage currently used (in bytes) + * @apiSuccess {String} global_storage_quota Maximum global storage (in bytes) * @apiPermission none * @apiVersion 1.5.0 * @@ -74,14 +76,24 @@ public class AppResource extends BaseResource { String currentVersion = configBundle.getString("api.current_version"); String minVersion = configBundle.getString("api.min_version"); Boolean guestLogin = ConfigUtil.getConfigBooleanValue(ConfigType.GUEST_LOGIN); + UserDao userDao = new UserDao(); + String globalQuotaStr = System.getenv(Constants.GLOBAL_QUOTA_ENV); + long globalQuota = 0; + if (!Strings.isNullOrEmpty(globalQuotaStr)) { + globalQuota = Long.valueOf(globalQuotaStr); + } JsonObjectBuilder response = Json.createObjectBuilder() .add("current_version", currentVersion.replace("-SNAPSHOT", "")) .add("min_version", minVersion) .add("guest_login", guestLogin) .add("total_memory", Runtime.getRuntime().totalMemory()) - .add("free_memory", Runtime.getRuntime().freeMemory()); - + .add("free_memory", Runtime.getRuntime().freeMemory()) + .add("global_storage_current", userDao.getGlobalStorageCurrent()); + if (globalQuota > 0) { + response.add("global_storage_quota", globalQuota); + } + return Response.ok().entity(response.build()).build(); } 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 e88491a0..cc208edd 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 @@ -3,6 +3,7 @@ package com.sismics.docs.rest.resource; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.io.ByteStreams; +import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.AclDao; import com.sismics.docs.core.dao.jpa.DocumentDao; @@ -14,7 +15,10 @@ import com.sismics.docs.core.event.FileCreatedAsyncEvent; import com.sismics.docs.core.event.FileDeletedAsyncEvent; import com.sismics.docs.core.model.jpa.File; import com.sismics.docs.core.model.jpa.User; -import com.sismics.docs.core.util.*; +import com.sismics.docs.core.util.DirectoryUtil; +import com.sismics.docs.core.util.EncryptionUtil; +import com.sismics.docs.core.util.FileUtil; +import com.sismics.docs.core.util.PdfUtil; import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ServerException; @@ -34,7 +38,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; @@ -130,11 +136,21 @@ public class FileResource extends BaseResource { throw new ServerException("ErrorGuessMime", "Error guessing mime type", e); } - // Validate quota + // Validate user quota if (user.getStorageCurrent() + fileSize > user.getStorageQuota()) { throw new ClientException("QuotaReached", "Quota limit reached"); } - + + // Validate global quota + String globalStorageQuotaStr = System.getenv(Constants.GLOBAL_QUOTA_ENV); + if (!Strings.isNullOrEmpty(globalStorageQuotaStr)) { + long globalStorageQuota = Long.valueOf(globalStorageQuotaStr); + long globalStorageCurrent = userDao.getGlobalStorageCurrent(); + if (globalStorageCurrent + fileSize > globalStorageQuota) { + throw new ClientException("QuotaReached", "Global quota limit reached"); + } + } + try { // Get files of this document FileDao fileDao = new FileDao(); diff --git a/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentDefault.js b/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentDefault.js index fd0e998b..21ad90e5 100644 --- a/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentDefault.js +++ b/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentDefault.js @@ -77,7 +77,7 @@ angular.module('docs').controller('DocumentDefault', function($scope, $rootScope }) .error(function (data) { newfile.status = $translate.instant('document.default.upload_error'); - if (data.type == 'QuotaReached') { + if (data.type === 'QuotaReached') { newfile.status += ' - ' + $translate.instant('document.default.upload_error_quota'); } }); diff --git a/docs-web/src/main/webapp/src/index.html b/docs-web/src/main/webapp/src/index.html index 3d237b6e..73af3dd6 100644 --- a/docs-web/src/main/webapp/src/index.html +++ b/docs-web/src/main/webapp/src/index.html @@ -157,6 +157,10 @@