From ca20785400f5091147831878075e68ed6c8611cb Mon Sep 17 00:00:00 2001 From: anhefti Date: Sun, 20 Jan 2019 16:28:55 +0100 Subject: [PATCH] created controller abstraction for Entity and ActivatableEntity --- .../ethz/seb/sebserver/gbl/model/Entity.java | 3 + .../seb/sebserver/gbl/model/EntityKey.java | 53 ++-- .../sebserver/gbl/model/EntityKeyAndName.java | 22 +- .../gbl/model/EntityProcessingReport.java | 38 ++- .../ch/ethz/seb/sebserver/gbl/model/Page.java | 10 +- .../seb/sebserver/gbl/model/exam/Exam.java | 2 +- .../sebserver/gbl/model/exam/QuizData.java | 5 - .../gbl/model/user/UserActivityLog.java | 6 + .../ch/ethz/seb/sebserver/gbl/util/Utils.java | 21 ++ .../servicelayer/PaginationService.java | 64 ++++- .../AuthorizationGrantService.java | 6 +- .../AuthorizationGrantServiceImpl.java | 84 ++++-- .../authorization/UserService.java | 6 + .../authorization/UserServiceImpl.java | 18 ++ .../bulkaction/BulkActionService.java | 71 ++--- .../servicelayer/dao/EntityDAO.java | 20 +- .../webservice/servicelayer/dao/ExamDAO.java | 6 +- .../servicelayer/dao/UserActivityLogDAO.java | 1 + .../webservice/servicelayer/dao/UserDAO.java | 8 +- .../servicelayer/dao/impl/ExamDAOImpl.java | 49 +++- .../dao/impl/InstitutionDAOImpl.java | 29 +-- .../dao/impl/LmsSetupDAOImpl.java | 43 ++- .../dao/impl/UserActivityLogDAOImpl.java | 98 +++---- .../servicelayer/dao/impl/UserDaoImpl.java | 24 +- .../servicelayer/lms/LmsAPIService.java | 6 +- .../lms/impl/LmsAPIServiceImpl.java | 10 +- .../api/ActivatableEntityController.java | 68 +++++ .../weblayer/api/EntityController.java | 68 +++++ .../api/ExamAdministrationController.java | 246 ++++++++++++++++++ .../weblayer/api/InstitutionController.java | 47 ++-- .../weblayer/api/LmsSetupController.java | 111 ++++---- .../weblayer/api/QuizImportController.java | 17 +- .../webservice/weblayer/api/RestAPI.java | 2 + .../weblayer/api/UserAccountController.java | 166 ++++++------ .../api/UserActivityLogController.java | 135 ++++++---- .../api/AdministrationAPIIntegrationTest.java | 8 + .../integration/api/UserAPITest.java | 211 +++++++++++---- .../api/UserActivityLogAPITest.java | 156 +++++++---- src/test/resources/data-test.sql | 10 +- 39 files changed, 1376 insertions(+), 572 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java index 4cbb04e2..caf5c045 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Entity.java @@ -10,6 +10,9 @@ package ch.ethz.seb.sebserver.gbl.model; public interface Entity extends ModelIdAware { + public static final String ATTR_ID = "id"; + public static final String ATTR_INSTITUTION = "institution"; + EntityType entityType(); String getName(); diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java index 66294db2..259d8aab 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKey.java @@ -8,35 +8,40 @@ package ch.ethz.seb.sebserver.gbl.model; -import javax.validation.constraints.NotNull; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; public class EntityKey { - public final String entityId; + @JsonProperty(value = "modelId", required = true) + public final String modelId; + @JsonProperty(value = "entityType", required = true) public final EntityType entityType; + @JsonIgnore public final boolean isIdPK; + @JsonCreator public EntityKey( - @NotNull final Long entityId, - @NotNull final EntityType entityType) { + @JsonProperty(value = "modelId", required = true) final String modelId, + @JsonProperty(value = "entityType", required = true) final EntityType entityType) { - this.entityId = String.valueOf(entityId); + this.modelId = modelId; + this.entityType = entityType; + this.isIdPK = entityType != EntityType.USER; + } + + public EntityKey( + final Long pk, + final EntityType entityType) { + + this.modelId = String.valueOf(pk); this.entityType = entityType; this.isIdPK = true; } - public EntityKey( - @NotNull final String entityId, - @NotNull final EntityType entityType, - final boolean isIdPK) { - - this.entityId = entityId; - this.entityType = entityType; - this.isIdPK = isIdPK; - } - - public String getEntityId() { - return this.entityId; + public String getModelId() { + return this.modelId; } public EntityType getEntityType() { @@ -47,8 +52,8 @@ public class EntityKey { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((this.entityId == null) ? 0 : this.entityId.hashCode()); result = prime * result + ((this.entityType == null) ? 0 : this.entityType.hashCode()); + result = prime * result + ((this.modelId == null) ? 0 : this.modelId.hashCode()); return result; } @@ -61,19 +66,19 @@ public class EntityKey { if (getClass() != obj.getClass()) return false; final EntityKey other = (EntityKey) obj; - if (this.entityId == null) { - if (other.entityId != null) - return false; - } else if (!this.entityId.equals(other.entityId)) - return false; if (this.entityType != other.entityType) return false; + if (this.modelId == null) { + if (other.modelId != null) + return false; + } else if (!this.modelId.equals(other.modelId)) + return false; return true; } @Override public String toString() { - return "EntityKey [entityId=" + this.entityId + ", entityType=" + this.entityType + "]"; + return "EntityKey [modelId=" + this.modelId + ", entityType=" + this.entityType + "]"; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java index bb37b491..7de9b769 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityKeyAndName.java @@ -19,7 +19,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { @JsonProperty(value = "entityType", required = true) public final EntityType entityType; @JsonProperty(value = Domain.ATTR_ID, required = true) - public final String id; + public final String modelId; @JsonProperty(value = "name", required = true) public final String name; @@ -30,14 +30,14 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { @JsonProperty(value = "name", required = true) final String name) { this.entityType = entityType; - this.id = id; + this.modelId = id; this.name = name; } public EntityKeyAndName(final EntityKey entityKey, final String name) { this.entityType = entityKey.entityType; - this.id = entityKey.entityId; + this.modelId = entityKey.modelId; this.name = name; } @@ -45,10 +45,6 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { return this.entityType; } - public String getId() { - return this.id; - } - @Override public String getName() { return this.name; @@ -57,7 +53,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { @Override @JsonIgnore public String getModelId() { - return this.id; + return this.modelId; } @Override @@ -65,7 +61,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { final int prime = 31; int result = 1; result = prime * result + ((this.entityType == null) ? 0 : this.entityType.hashCode()); - result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); + result = prime * result + ((this.modelId == null) ? 0 : this.modelId.hashCode()); result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); return result; } @@ -81,10 +77,10 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { final EntityKeyAndName other = (EntityKeyAndName) obj; if (this.entityType != other.entityType) return false; - if (this.id == null) { - if (other.id != null) + if (this.modelId == null) { + if (other.modelId != null) return false; - } else if (!this.id.equals(other.id)) + } else if (!this.modelId.equals(other.modelId)) return false; if (this.name == null) { if (other.name != null) @@ -96,7 +92,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware { @Override public String toString() { - return "EntityIdAndName [entityType=" + this.entityType + ", id=" + this.id + ", name=" + this.name + "]"; + return "EntityIdAndName [entityType=" + this.entityType + ", id=" + this.modelId + ", name=" + this.name + "]"; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java index d26fd8fd..11827b04 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/EntityProcessingReport.java @@ -9,30 +9,46 @@ package ch.ethz.seb.sebserver.gbl.model; import java.util.Collection; -import java.util.Collections; -import java.util.Map; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.util.Utils; + public class EntityProcessingReport { @JsonProperty(value = "source", required = true) - public final Collection source; + public final Collection source; @JsonProperty(value = "dependencies", required = true) - public final Collection dependencies; + public final Collection dependencies; @JsonProperty(value = "errors", required = true) - public final Map errors; + public final Collection errors; @JsonCreator public EntityProcessingReport( - @JsonProperty(value = "source", required = true) final Collection source, - @JsonProperty(value = "dependencies", required = true) final Collection dependencies, - @JsonProperty(value = "errors", required = true) final Map errors) { + @JsonProperty(value = "source", required = true) final Collection source, + @JsonProperty(value = "dependencies", required = true) final Collection dependencies, + @JsonProperty(value = "errors", required = true) final Collection errors) { + + this.source = Utils.immutableCollectionOf(source); + this.dependencies = Utils.immutableCollectionOf(dependencies); + this.errors = Utils.immutableCollectionOf(errors); + } + + public static final class ErrorEntry { + + public final EntityKey entityKey; + public final APIMessage errorMessage; + + @JsonCreator + public ErrorEntry( + @JsonProperty(value = "entity_key", required = true) final EntityKey entityKey, + @JsonProperty(value = "error_message", required = true) final APIMessage errorMessage) { + + this.entityKey = entityKey; + this.errorMessage = errorMessage; + } - this.source = Collections.unmodifiableCollection(source); - this.dependencies = Collections.unmodifiableCollection(dependencies); - this.errors = Collections.unmodifiableMap(errors); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Page.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Page.java index 5de2b4ab..3ec090f3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/Page.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/Page.java @@ -22,11 +22,11 @@ public final class Page { DESCENDING } - public static final String ATTR_NUMBER_OF_PAGES = "numberOfPages"; - public static final String ATTR_PAGE_NUMBER = "pageNumber"; - public static final String ATTR_PAGE_SIZE = "pageSize"; - public static final String ATTR_SORT_BY = "sortBy"; - public static final String ATTR_SORT_ORDER = "sortOrder"; + public static final String ATTR_NUMBER_OF_PAGES = "number_of_pages"; + public static final String ATTR_PAGE_NUMBER = "page_number"; + public static final String ATTR_PAGE_SIZE = "page_size"; + public static final String ATTR_SORT_BY = "sort_by"; + public static final String ATTR_SORT_ORDER = "sort_order"; public static final String ATTR_CONTENT = "content"; @JsonProperty(ATTR_NUMBER_OF_PAGES) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java index c8e21acb..fa1def4f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java @@ -26,7 +26,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; public final class Exam implements GrantEntity, Activatable { - public static final String FILTER_ATTR_INSTITUTION = "institution"; + public static final String FILTER_ATTR_INSTITUTION = ATTR_INSTITUTION; public static final String FILTER_ATTR_LMS_SETUP = "lms_setup"; public static final String FILTER_ATTR_NAME = "name_like"; public static final String FILTER_ATTR_STATUS = "status"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/QuizData.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/QuizData.java index 6c963bb0..b7967c9d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/QuizData.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/QuizData.java @@ -22,11 +22,6 @@ public final class QuizData { public static final String FILTER_ATTR_NAME = "name_like"; public static final String FILTER_ATTR_START_TIME = "start_timestamp"; - public static final String PAGE_ATTR_NUMBER = "page_number"; - public static final String PAGE_ATTR_SIZE = "page_size"; - public static final String PAGE_ATTR_SORT_BY = "sort_by"; - public static final String PAGE_ATTR_SORT_ORDER = "sort_order"; - public static final String QUIZ_ATTR_ID = "quiz_id"; public static final String QUIZ_ATTR_NAME = "quiz_name"; public static final String QUIZ_ATTR_DESCRIPTION = "quiz_description"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java index ed5c7fa9..35537b15 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserActivityLog.java @@ -19,6 +19,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.Acti public class UserActivityLog implements Entity { + public static final String FILTER_ATTR_INSTITUTION = ATTR_INSTITUTION; + public static final String FILTER_ATTR_FROM = "from"; + public static final String FILTER_ATTR_TO = "to"; + public static final String FILTER_ATTR_ACTIVITY_TYPES = "activity_types"; + public static final String FILTER_ATTR_ENTITY_TYPES = "entity_types"; + @JsonIgnore public final Long id; @JsonProperty(USER_ACTIVITY_LOG.ATTR_USER_UUID) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index 9d782063..eeda6768 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -18,6 +18,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import ch.ethz.seb.sebserver.gbl.Constants; @@ -105,4 +106,24 @@ public final class Utils { return map; } + public static DateTime toDateTime(final String dateString) { + if (StringUtils.isBlank(dateString)) { + return null; + } + + if (dateString.contains(".")) { + return DateTime.parse(dateString, Constants.DATE_TIME_PATTERN_UTC_MILLIS); + } else { + return DateTime.parse(dateString, Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); + } + } + + public static Long toMilliSeconds(final String dateString) { + if (StringUtils.isBlank(dateString)) { + return null; + } + + return toDateTime(dateString).getMillis(); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java index 9880f4fe..0c9e13be 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/PaginationService.java @@ -25,6 +25,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserActivityLogRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; @@ -50,6 +51,25 @@ public class PaginationService { initSortColumnMapping(); } + /** Use this to verify whether native sorting (on SQL level) is supported for a given orderBy column + * and a given SqlTable or not. + * + * @param table SqlTable the SQL table (MyBatis) + * @param orderBy the orderBy columnName + * @return true if there is native sorting support for the given attributes */ + public boolean isNativeSortingSupported(final SqlTable table, final String orderBy) { + if (StringUtils.isBlank(orderBy)) { + return false; + } + + final Map tableMap = this.sortColumnMapping.get(table.name()); + if (tableMap == null) { + return false; + } + + return tableMap.containsKey(orderBy); + } + public void setDefaultLimitOfNotSet(final SqlTable table) { if (PageHelper.getLocalPage() != null) { return; @@ -69,6 +89,20 @@ public class PaginationService { setPagination(1, this.maxPageSize, sortBy, sortOrder, table); } + public int getPageNumber(final Integer pageNumber) { + return (pageNumber == null) + ? 1 + : pageNumber; + } + + public int getPageSize(final Integer pageSize) { + return (pageSize == null || pageSize < 0) + ? this.defaultPageSize + : (pageSize > this.maxPageSize) + ? this.maxPageSize + : pageSize; + } + public com.github.pagehelper.Page setPagination( final Integer pageNumber, final Integer pageSize, @@ -76,18 +110,8 @@ public class PaginationService { final Page.SortOrder sortOrder, final SqlTable table) { - final int _pageNumber = (pageNumber == null) - ? 1 - : pageNumber; - - final int _pageSize = (pageSize == null || pageSize < 0) - ? this.defaultPageSize - : (pageSize > this.maxPageSize) - ? this.maxPageSize - : pageSize; - final com.github.pagehelper.Page startPage = - PageHelper.startPage(_pageNumber, _pageSize, true, true, false); + PageHelper.startPage(getPageNumber(pageNumber), getPageSize(pageSize), true, true, false); final String sortColumnName = verifySortColumnName(sortBy, table); if (StringUtils.isNoneBlank(sortColumnName)) { @@ -111,7 +135,7 @@ public class PaginationService { final com.github.pagehelper.Page page = setPagination(pageNumber, pageSize, sortBy, sortOrder, table); final Collection pageList = delegate.get(); - return new Page<>(page.getPages(), pageNumber, sortBy, sortOrder, pageList); + return new Page<>(page.getPages(), page.getPageNum(), sortBy, sortOrder, pageList); } private String verifySortColumnName(final String sortBy, final SqlTable table) { @@ -130,6 +154,7 @@ public class PaginationService { // TODO is it possible to generate this within MyBatis generator? private void initSortColumnMapping() { + // User Table final Map userTableMap = new HashMap<>(); userTableMap.put(Domain.USER.ATTR_NAME, UserRecordDynamicSqlSupport.name.name()); @@ -163,6 +188,21 @@ public class PaginationService { UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord.name(), Domain.USER_ACTIVITY_LOG.ATTR_ID); + // Exam Table + final Map examTableMap = new HashMap<>(); + examTableMap.put( + Domain.EXAM.ATTR_INSTITUTION_ID, + ExamRecordDynamicSqlSupport.institutionId.name()); + examTableMap.put( + Domain.EXAM.ATTR_LMS_SETUP_ID, + ExamRecordDynamicSqlSupport.lmsSetupId.name()); + examTableMap.put( + Domain.EXAM.ATTR_TYPE, + ExamRecordDynamicSqlSupport.type.name()); + examTableMap.put( + Domain.EXAM.ATTR_STATUS, + ExamRecordDynamicSqlSupport.status.name()); + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java index 65c10072..394eec7c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantService.java @@ -22,7 +22,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; public interface AuthorizationGrantService { /** Gets the UserService that is bundled within the AuthorizationGrantService - * + * * @return the UserService that is bundled within the AuthorizationGrantService */ UserService getUserService(); @@ -33,6 +33,10 @@ public interface AuthorizationGrantService { * @param privilegeType the PrivilegeType to check on EntityType */ void checkHasAnyPrivilege(EntityType entityType, PrivilegeType privilegeType); + void checkPrivilege(EntityType entityType, PrivilegeType privilegeType, Long institutionId); + + void checkPrivilege(EntityType entityType, PrivilegeType privilegeType, Long institutionId, Long ownerId); + /** Check if current user has grant on a given GrantEntity instance for specified PrivilegeType * * @param entity The GrantEntity to check specified PrivilegeType for diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java index f752f108..5ebc5d52 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationGrantServiceImpl.java @@ -120,49 +120,86 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService } @Override - public void checkHasAnyPrivilege(final EntityType entityType, final PrivilegeType grantType) { + public void checkHasAnyPrivilege(final EntityType entityType, final PrivilegeType privilegeType) { final SEBServerUser currentUser = this.userService.getCurrentUser(); - if (hasBasePrivilege(entityType, grantType, currentUser) || - hasInstitutionalPrivilege(entityType, grantType, currentUser)) { + if (hasBasePrivilege(entityType, privilegeType, currentUser) || + hasInstitutionalPrivilege(entityType, privilegeType, currentUser)) { return; } - throw new PermissionDeniedException(entityType, grantType, currentUser.getUserInfo().uuid); + throw new PermissionDeniedException(entityType, privilegeType, currentUser.getUserInfo().uuid); } @Override - public Result checkGrantOnEntity(final E entity, final PrivilegeType grantType) { + public void checkPrivilege( + final EntityType entityType, + final PrivilegeType privilegeType, + final Long institutionId) { final SEBServerUser currentUser = this.userService.getCurrentUser(); - if (hasGrant(entity, grantType, currentUser)) { + if (hasBasePrivilege(entityType, privilegeType, currentUser)) { + return; + } + + if (institutionId == null) { + throw new PermissionDeniedException(entityType, privilegeType, currentUser.getUserInfo().uuid); + } + + if (hasInstitutionalPrivilege(entityType, privilegeType, currentUser) && + currentUser.institutionId().longValue() == institutionId.longValue()) { + return; + } + + throw new PermissionDeniedException(entityType, privilegeType, currentUser.getUserInfo().uuid); + } + + @Override + public void checkPrivilege( + final EntityType entityType, + final PrivilegeType privilegeType, + final Long institutionId, + final Long ownerId) { + + final SEBServerUser currentUser = this.userService.getCurrentUser(); + + // TODO Auto-generated method stub + + throw new PermissionDeniedException(entityType, privilegeType, currentUser.getUserInfo().uuid); + } + + @Override + public Result checkGrantOnEntity(final E entity, final PrivilegeType privilegeType) { + + final SEBServerUser currentUser = this.userService.getCurrentUser(); + if (hasGrant(entity, privilegeType, currentUser)) { return Result.of(entity); } else { - return Result.ofError(new PermissionDeniedException(entity, grantType, currentUser.getUserInfo().uuid)); + return Result.ofError(new PermissionDeniedException(entity, privilegeType, currentUser.getUserInfo().uuid)); } } @Override - public boolean hasBasePrivilege(final EntityType entityType, final PrivilegeType grantType) { - return hasBasePrivilege(entityType, grantType, this.userService.getCurrentUser()); + public boolean hasBasePrivilege(final EntityType entityType, final PrivilegeType privilegeType) { + return hasBasePrivilege(entityType, privilegeType, this.userService.getCurrentUser()); } @Override public boolean hasBasePrivilege( final EntityType entityType, - final PrivilegeType grantType, + final PrivilegeType privilegeType, final Principal principal) { - return hasBasePrivilege(entityType, grantType, this.userService.extractFromPrincipal(principal)); + return hasBasePrivilege(entityType, privilegeType, this.userService.extractFromPrincipal(principal)); } private boolean hasBasePrivilege( final EntityType entityType, - final PrivilegeType grantType, + final PrivilegeType privilegeType, final SEBServerUser user) { for (final UserRole role : user.getUserRoles()) { final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role)); - if (roleTypeGrant != null && roleTypeGrant.hasBasePrivilege(grantType)) { + if (roleTypeGrant != null && roleTypeGrant.hasBasePrivilege(privilegeType)) { return true; } } @@ -171,27 +208,36 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService } @Override - public boolean hasInstitutionalPrivilege(final EntityType entityType, final PrivilegeType grantType) { - return hasInstitutionalPrivilege(entityType, grantType, this.userService.getCurrentUser()); + public boolean hasInstitutionalPrivilege( + final EntityType entityType, + final PrivilegeType privilegeType) { + + return hasInstitutionalPrivilege( + entityType, + privilegeType, + this.userService.getCurrentUser()); } @Override public boolean hasInstitutionalPrivilege( final EntityType entityType, - final PrivilegeType grantType, + final PrivilegeType privilegeType, final Principal principal) { - return hasInstitutionalPrivilege(entityType, grantType, this.userService.extractFromPrincipal(principal)); + return hasInstitutionalPrivilege( + entityType, + privilegeType, + this.userService.extractFromPrincipal(principal)); } private boolean hasInstitutionalPrivilege( final EntityType entityType, - final PrivilegeType grantType, + final PrivilegeType privilegeType, final SEBServerUser user) { for (final UserRole role : user.getUserRoles()) { final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role)); - if (roleTypeGrant != null && roleTypeGrant.hasInstitutionalPrivilege(grantType)) { + if (roleTypeGrant != null && roleTypeGrant.hasInstitutionalPrivilege(privilegeType)) { return true; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java index 9b7b1f53..6dcc396d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserService.java @@ -10,9 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization; import java.security.Principal; +import org.springframework.web.bind.WebDataBinder; + /** A service to get the authenticated user from current request */ public interface UserService { + String USERS_INSTITUTION_AS_DEFAULT = "USERS_INSTITUTION_AS_DEFAULT"; + /** Use this to get the current User within a request-response thread cycle. * * @return the SEBServerUser instance of the current request @@ -46,4 +50,6 @@ public interface UserService { * @return an overall super user with all rights */ SEBServerUser getSuperUser(); + void addUsersInstitutionDefaultPropertySupport(final WebDataBinder binder); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java index c7ffe952..d74c1394 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/UserServiceImpl.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization; +import java.beans.PropertyEditorSupport; import java.security.Principal; import java.util.Collection; @@ -20,6 +21,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; +import org.springframework.web.bind.WebDataBinder; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; @@ -78,6 +80,22 @@ public class UserServiceImpl implements UserService { return SUPER_USER; } + @Override + public void addUsersInstitutionDefaultPropertySupport(final WebDataBinder binder) { + final PropertyEditorSupport usersInstitutionDefaultEditor = new PropertyEditorSupport() { + @Override + public void setAsText(final String text) throws IllegalArgumentException { + if (UserService.USERS_INSTITUTION_AS_DEFAULT.equals(text)) { + setValue(getCurrentUser().institutionId()); + } else { + setValue((text == null) ? null : Long.decode(text)); + } + } + }; + binder.registerCustomEditor(Long.class, usersInstitutionDefaultEditor); + + } + // 1. OAuth2Authentication strategy @Lazy @Component diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java index a0b03f02..0787acc5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java @@ -21,6 +21,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; @Service @@ -49,44 +50,56 @@ public class BulkActionService { action.alreadyProcessed = true; } - public void doBulkAction(final BulkAction action) { - checkProcessing(action); + public Result doBulkAction(final BulkAction action) { + return Result.tryCatch(() -> { - final BulkActionSupportDAO supportForSource = this.supporter.get(action.sourceType); - if (supportForSource == null) { - action.alreadyProcessed = true; - return; - } + checkProcessing(action); - collectDependencies(action); - - if (!action.dependencies.isEmpty()) { - // process dependencies first... - final List> dependancySupporter = - getDependancySupporter(action); - - for (final BulkActionSupportDAO support : dependancySupporter) { - action.result.addAll(support.processBulkAction(action)); + final BulkActionSupportDAO supportForSource = this.supporter + .get(action.sourceType); + if (supportForSource == null) { + action.alreadyProcessed = true; + throw new IllegalArgumentException("No bulk action support for: " + action); } - } - action.result.addAll(supportForSource.processBulkAction(action)); + collectDependencies(action); - processUserActivityLog(action); - action.alreadyProcessed = true; + if (!action.dependencies.isEmpty()) { + // process dependencies first... + final List> dependancySupporter = + getDependancySupporter(action); + + for (final BulkActionSupportDAO support : dependancySupporter) { + action.result.addAll(support.processBulkAction(action)); + } + } + + action.result.addAll(supportForSource.processBulkAction(action)); + + processUserActivityLog(action); + action.alreadyProcessed = true; + return action; + }); } - public EntityProcessingReport createReport(final BulkAction action) { + public Result createReport(final BulkAction action) { if (!action.alreadyProcessed) { - doBulkAction(action); + return doBulkAction(action) + .flatMap(this::createFullReport); + } else { + return createFullReport(action); } + } - // TODO + private Result createFullReport(final BulkAction action) { + return Result.tryCatch(() -> { - return new EntityProcessingReport( - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyMap()); + // TODO + return new EntityProcessingReport( + action.sources, + Collections.emptyList(), + Collections.emptyList()); + }); } private void processUserActivityLog(final BulkAction action) { @@ -98,7 +111,7 @@ public class BulkActionService { this.userActivityLogDAO.log( action.type.activityType, key.entityType, - key.entityId, + key.modelId, "bulk action dependency"); } @@ -106,7 +119,7 @@ public class BulkActionService { this.userActivityLogDAO.log( action.type.activityType, key.entityType, - key.entityId, + key.modelId, "bulk action source"); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java index 8858cfd1..58fd7295 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java @@ -32,7 +32,21 @@ public interface EntityDAO { * @param id the data base identifier of the entity * @return Result refer the Entity instance with the specified database identifier or refer to an error if * happened */ - Result byId(Long id); + Result byPK(Long id); + + /** Use this to get an Entity instance of concrete type by model identifier + * + * NOTE: A model identifier may differ from the string representation of the database identifier + * but usually they are the same. + * + * @param id the model identifier + * @return Result refer the Entity instance with the specified model identifier or refer to an error if + * happened */ + default Result byModelId(final String id) { + return Result.tryCatch(() -> { + return Long.parseLong(id); + }).flatMap(this::byPK); + } /** Use this to get a Collection of all entities of concrete type that matches a given predicate. * @@ -104,7 +118,7 @@ public interface EntityDAO { * * @param keys Collection of EntityKey of various types * @return List of id's (PK's) from the given key collection that match the concrete EntityType */ - default List extractIdsFromKeys(final Collection keys) { + default List extractPKsFromKeys(final Collection keys) { if (keys == null) { return Collections.emptyList(); @@ -114,7 +128,7 @@ public interface EntityDAO { return keys .stream() .filter(key -> key.entityType == entityType) - .map(key -> Long.valueOf(key.entityId)) + .map(key -> Long.valueOf(key.modelId)) .collect(Collectors.toList()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java index 1d4fcab9..569678bb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamDAO.java @@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import java.util.Collection; +import org.joda.time.DateTime; + import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -27,8 +29,10 @@ public interface ExamDAO extends ActivatableEntityDAO, BulkActionSupportDA String name, Exam.ExamStatus status, Exam.ExamType type, - Long startTime, + DateTime from, String owner, Boolean active); + Result save(Exam exam); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java index f221b542..1deff9b3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserActivityLogDAO.java @@ -84,6 +84,7 @@ public interface UserActivityLogDAO extends UserRelatedEntityDAO> all( + Long InstitutionId, String userId, Long from, Long to, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java index 32b57eac..8b31aa39 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java @@ -26,17 +26,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSuppor * within SEBServerUser. */ public interface UserDAO extends ActivatableEntityDAO, BulkActionSupportDAO { - /** Use this to get UserInfo by users UUID - * - * @param uuid The UUID of the user to get UserInfo from - * @return a Result of UserInfo data from user with the specified UUID. Or an exception result on error case */ - Result byUuid(String uuid); - /** Use this to get the user id (PK) from a given UUID. * * @param uuid The UUID of the user * @return the user id (PK) from a given UUID. */ - Result pkForUUID(String uuid); + Result pkForModelId(String uuid); /** Use this to get UserInfo by users username * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index 6e303d15..df0ce7f4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -22,6 +22,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.QueryExpressionDSL; import org.springframework.context.annotation.Lazy; @@ -67,7 +68,7 @@ public class ExamDAOImpl implements ExamDAO { @Override @Transactional(readOnly = true) - public Result byId(final Long id) { + public Result byPK(final Long id) { return recordById(id) .flatMap(this::toDomainModel); } @@ -108,7 +109,7 @@ public class ExamDAOImpl implements ExamDAO { final String name, final ExamStatus status, final ExamType type, - final Long startTime, + final DateTime from, final String owner, final Boolean active) { @@ -121,8 +122,8 @@ public class ExamDAOImpl implements ExamDAO { } } - if (startTime != null) { - if (exam.startTime.getMillis() < startTime.longValue()) { + if (from != null) { + if (exam.startTime.isAfter(from)) { return false; } } @@ -167,10 +168,40 @@ public class ExamDAOImpl implements ExamDAO { return Result.ofTODO(); } + @Override + public Result save(final Exam exam) { + if (exam == null) { + return Result.ofError(new NullPointerException("exam has null-reference")); + } + if (exam.id == null) { + return Result.ofError(new IllegalArgumentException("exam.id has null-reference")); + } + + return update(exam) + .flatMap(this::toDomainModel); + } + + private Result update(final Exam exam) { + return Result.tryCatch(() -> { + final ExamRecord examRecord = new ExamRecord( + exam.id, + null, null, null, null, + (exam.supporter != null) + ? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR) + : null, + (exam.type != null) ? exam.type.name() : null, + (exam.status != null) ? exam.status.name() : null, + BooleanUtils.toIntegerObject(exam.active)); + + this.examRecordMapper.updateByPrimaryKeySelective(examRecord); + return this.examRecordMapper.selectByPrimaryKey(exam.id); + }); + } + @Override @Transactional public Collection> setActive(final Set all, final boolean active) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); final ExamRecord examRecord = new ExamRecord(null, null, null, null, null, null, null, null, BooleanUtils.toInteger(active)); @@ -196,7 +227,7 @@ public class ExamDAOImpl implements ExamDAO { @Override @Transactional public Collection> delete(final Set all) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); try { @@ -235,7 +266,7 @@ public class ExamDAOImpl implements ExamDAO { @Transactional(readOnly = true) public Result> bulkLoadEntities(final Collection keys) { return Result.tryCatch(() -> { - final List ids = extractIdsFromKeys(keys); + final List ids = extractPKsFromKeys(keys); return this.examRecordMapper.selectByExample() .where(ExamRecordDynamicSqlSupport.id, isIn(ids)) .build() @@ -247,7 +278,7 @@ public class ExamDAOImpl implements ExamDAO { return Result.tryCatch(() -> { return this.examRecordMapper.selectIdsByExample() .where(ExamRecordDynamicSqlSupport.institutionId, - isEqualTo(Long.valueOf(institutionKey.entityId))) + isEqualTo(Long.valueOf(institutionKey.modelId))) .build() .execute() .stream() @@ -260,7 +291,7 @@ public class ExamDAOImpl implements ExamDAO { return Result.tryCatch(() -> { return this.examRecordMapper.selectIdsByExample() .where(ExamRecordDynamicSqlSupport.lmsSetupId, - isEqualTo(Long.valueOf(lmsSetupKey.entityId))) + isEqualTo(Long.valueOf(lmsSetupKey.modelId))) .build() .execute() .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java index bdaf2409..fc6e60ce 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/InstitutionDAOImpl.java @@ -56,7 +56,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { @Override @Transactional(readOnly = true) - public Result byId(final Long id) { + public Result byPK(final Long id) { return recordById(id) .flatMap(InstitutionDAOImpl::toDomainModel); } @@ -123,7 +123,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { @Override @Transactional public Collection> setActive(final Set all, final boolean active) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); final InstitutionRecord institutionRecord = new InstitutionRecord( null, null, null, BooleanUtils.toInteger(active), null); @@ -147,7 +147,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { @Override @Transactional public Collection> delete(final Set all) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); try { this.institutionRecordMapper.deleteByExample() @@ -177,7 +177,7 @@ public class InstitutionDAOImpl implements InstitutionDAO { @Transactional(readOnly = true) public Result> bulkLoadEntities(final Collection keys) { return Result.tryCatch(() -> { - final List ids = extractIdsFromKeys(keys); + final List ids = extractPKsFromKeys(keys); return this.institutionRecordMapper.selectByExample() .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) @@ -217,19 +217,18 @@ public class InstitutionDAOImpl implements InstitutionDAO { } private Result update(final Institution institution) { - return recordById(institution.id) - .map(record -> { + return Result.tryCatch(() -> { - final InstitutionRecord newRecord = new InstitutionRecord( - institution.id, - institution.name, - institution.urlSuffix, - null, - institution.logoImage); + final InstitutionRecord newRecord = new InstitutionRecord( + institution.id, + institution.name, + institution.urlSuffix, + null, + institution.logoImage); - this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord); - return this.institutionRecordMapper.selectByPrimaryKey(institution.id); - }); + this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord); + return this.institutionRecordMapper.selectByPrimaryKey(institution.id); + }); } private static Result toDomainModel(final InstitutionRecord record) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java index b2c13202..53ad58e4 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java @@ -55,7 +55,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { @Override @Transactional(readOnly = true) - public Result byId(final Long id) { + public Result byPK(final Long id) { return recordById(id) .flatMap(LmsSetupDAOImpl::toDomainModel); } @@ -137,7 +137,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { @Override @Transactional public Collection> setActive(final Set all, final boolean active) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); final LmsSetupRecord lmsSetupRecord = new LmsSetupRecord( null, null, null, null, null, null, null, null, null, null, BooleanUtils.toIntegerObject(active)); @@ -162,7 +162,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { @Override @Transactional public Collection> delete(final Set all) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); try { this.lmsSetupRecordMapper.deleteByExample() @@ -196,7 +196,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { @Transactional(readOnly = true) public Result> bulkLoadEntities(final Collection keys) { return Result.tryCatch(() -> { - final List ids = extractIdsFromKeys(keys); + final List ids = extractPKsFromKeys(keys); return this.lmsSetupRecordMapper.selectByExample() .where(LmsSetupRecordDynamicSqlSupport.id, isIn(ids)) @@ -213,7 +213,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { return Result.tryCatch(() -> { return this.lmsSetupRecordMapper.selectIdsByExample() .where(LmsSetupRecordDynamicSqlSupport.institutionId, - isEqualTo(Long.valueOf(institutionKey.entityId))) + isEqualTo(Long.valueOf(institutionKey.modelId))) .build() .execute() .stream() @@ -271,25 +271,24 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { } private Result update(final LmsSetup lmsSetup) { - return recordById(lmsSetup.id) - .map(record -> { + return Result.tryCatch(() -> { - final LmsSetupRecord newRecord = new LmsSetupRecord( - lmsSetup.id, - lmsSetup.institutionId, - lmsSetup.name, - (lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null, - lmsSetup.lmsApiUrl, - lmsSetup.lmsAuthName, - lmsSetup.lmsAuthSecret, - lmsSetup.lmsRestApiToken, - lmsSetup.sebAuthName, - lmsSetup.sebAuthSecret, - null); + final LmsSetupRecord newRecord = new LmsSetupRecord( + lmsSetup.id, + lmsSetup.institutionId, + lmsSetup.name, + (lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null, + lmsSetup.lmsApiUrl, + lmsSetup.lmsAuthName, + lmsSetup.lmsAuthSecret, + lmsSetup.lmsRestApiToken, + lmsSetup.sebAuthName, + lmsSetup.sebAuthSecret, + null); - this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord); - return this.lmsSetupRecordMapper.selectByPrimaryKey(lmsSetup.id); - }); + this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord); + return this.lmsSetupRecordMapper.selectByPrimaryKey(lmsSetup.id); + }); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java index fa17066c..495980af 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserActivityLogDAOImpl.java @@ -34,8 +34,6 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserActivityLogRe import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.UserActivityLogRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; -import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; -import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; @@ -49,18 +47,15 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { private final UserActivityLogRecordMapper userLogRecordMapper; private final UserService userService; - private final AuthorizationGrantService authorizationGrantService; private final PaginationService paginationService; public UserActivityLogDAOImpl( final UserActivityLogRecordMapper userLogRecordMapper, final UserService userService, - final AuthorizationGrantService authorizationGrantService, final PaginationService paginationService) { this.userLogRecordMapper = userLogRecordMapper; this.userService = userService; - this.authorizationGrantService = authorizationGrantService; this.paginationService = paginationService; } @@ -172,7 +167,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { @Override @Transactional(readOnly = true) - public Result byId(final Long id) { + public Result byPK(final Long id) { return Result.tryCatch(() -> this.userLogRecordMapper.selectByPrimaryKey(id)) .flatMap(UserActivityLogDAOImpl::toDomainModel); } @@ -180,7 +175,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { @Override @Transactional public Collection> delete(final Set all) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); try { this.userLogRecordMapper.deleteByExample() @@ -202,12 +197,24 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { @Override @Transactional(readOnly = true) public Result> getAllForUser(final String userUuid) { - return all(userUuid, null, null, model -> true); + return Result.tryCatch(() -> { + return this.userLogRecordMapper.selectByExample() + .where( + UserActivityLogRecordDynamicSqlSupport.userUuid, + SqlBuilder.isEqualTo(userUuid)) + .build() + .execute() + .stream() + .map(UserActivityLogDAOImpl::toDomainModel) + .flatMap(Result::skipOnError) + .collect(Collectors.toList()); + }); } @Override @Transactional(readOnly = true) public Result> all( + final Long institutionId, final String userId, final Long from, final Long to, @@ -218,57 +225,30 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO { ? predicate : model -> true; - final boolean basePrivilege = this.authorizationGrantService.hasBasePrivilege( - EntityType.USER_ACTIVITY_LOG, - PrivilegeType.READ_ONLY); - - final Long institutionId = (basePrivilege) - ? null - : this.userService.getCurrentUser().institutionId(); - - return (institutionId == null) - ? this.userLogRecordMapper.selectByExample() - .where( - UserActivityLogRecordDynamicSqlSupport.userUuid, - SqlBuilder.isEqualToWhenPresent(userId)) - .and( - UserActivityLogRecordDynamicSqlSupport.timestamp, - SqlBuilder.isGreaterThanOrEqualToWhenPresent(from)) - .and( - UserActivityLogRecordDynamicSqlSupport.timestamp, - SqlBuilder.isLessThanWhenPresent(to)) - .build() - .execute() - .stream() - .filter(_predicate) - .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(Result::skipOnError) - .collect(Collectors.toList()) - - : this.userLogRecordMapper.selectByExample() - .join(UserRecordDynamicSqlSupport.userRecord) - .on( - UserRecordDynamicSqlSupport.uuid, - SqlBuilder.equalTo(UserActivityLogRecordDynamicSqlSupport.userUuid)) - .where( - UserActivityLogRecordDynamicSqlSupport.userUuid, - SqlBuilder.isEqualToWhenPresent(userId)) - .and( - UserRecordDynamicSqlSupport.institutionId, - SqlBuilder.isEqualToWhenPresent(institutionId)) - .and( - UserActivityLogRecordDynamicSqlSupport.timestamp, - SqlBuilder.isGreaterThanOrEqualToWhenPresent(from)) - .and( - UserActivityLogRecordDynamicSqlSupport.timestamp, - SqlBuilder.isLessThanWhenPresent(to)) - .build() - .execute() - .stream() - .filter(_predicate) - .map(UserActivityLogDAOImpl::toDomainModel) - .flatMap(Result::skipOnError) - .collect(Collectors.toList()); + return this.userLogRecordMapper.selectByExample() + .join(UserRecordDynamicSqlSupport.userRecord) + .on( + UserRecordDynamicSqlSupport.uuid, + SqlBuilder.equalTo(UserActivityLogRecordDynamicSqlSupport.userUuid)) + .where( + UserRecordDynamicSqlSupport.institutionId, + SqlBuilder.isEqualTo(institutionId)) + .and( + UserActivityLogRecordDynamicSqlSupport.userUuid, + SqlBuilder.isEqualToWhenPresent(userId)) + .and( + UserActivityLogRecordDynamicSqlSupport.timestamp, + SqlBuilder.isGreaterThanOrEqualToWhenPresent(from)) + .and( + UserActivityLogRecordDynamicSqlSupport.timestamp, + SqlBuilder.isLessThanWhenPresent(to)) + .build() + .execute() + .stream() + .filter(_predicate) + .map(UserActivityLogDAOImpl::toDomainModel) + .flatMap(Result::skipOnError) + .collect(Collectors.toList()); }); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java index 788dce87..4f837778 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDaoImpl.java @@ -83,22 +83,22 @@ public class UserDaoImpl implements UserDAO { @Override @Transactional(readOnly = true) - public Result byId(final Long id) { + public Result byPK(final Long id) { return Result.tryCatch(() -> this.userRecordMapper.selectByPrimaryKey(id)) .flatMap(this::toDomainModel); } @Override @Transactional(readOnly = true) - public Result byUuid(final String uuid) { - return recordByUUID(uuid) + public Result byModelId(final String modelId) { + return recordByUUID(modelId) .flatMap(this::toDomainModel); } @Override @Transactional(readOnly = true) - public Result pkForUUID(final String uuid) { - return recordByUUID(uuid) + public Result pkForModelId(final String modelId) { + return recordByUUID(modelId) .map(r -> r.getId()); } @@ -191,7 +191,7 @@ public class UserDaoImpl implements UserDAO { @Override @Transactional public Collection> setActive(final Set all, final boolean active) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); final UserRecord userRecord = new UserRecord( null, null, null, null, null, null, null, null, null, BooleanUtils.toIntegerObject(active)); @@ -216,7 +216,7 @@ public class UserDaoImpl implements UserDAO { @Override @Transactional public Collection> delete(final Set all) { - final List ids = extractIdsFromKeys(all); + final List ids = extractPKsFromKeys(all); try { this.userRecordMapper.deleteByExample() @@ -259,7 +259,7 @@ public class UserDaoImpl implements UserDAO { @Transactional(readOnly = true) public Result> bulkLoadEntities(final Collection keys) { return Result.tryCatch(() -> { - final List ids = extractIdsFromKeys(keys); + final List ids = extractPKsFromKeys(keys); return this.userRecordMapper.selectByExample() .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) @@ -273,12 +273,12 @@ public class UserDaoImpl implements UserDAO { } @Override - public List extractIdsFromKeys(final Collection keys) { + public List extractPKsFromKeys(final Collection keys) { if (keys == null || keys.isEmpty() || keys.iterator().next().isIdPK) { - return UserDAO.super.extractIdsFromKeys(keys); + return UserDAO.super.extractPKsFromKeys(keys); } else { final List uuids = keys.stream() - .map(key -> key.entityId) + .map(key -> key.modelId) .collect(Collectors.toList()); try { @@ -299,7 +299,7 @@ public class UserDaoImpl implements UserDAO { return Result.tryCatch(() -> { return this.userRecordMapper.selectIdsByExample() .where(UserRecordDynamicSqlSupport.institutionId, - isEqualTo(Long.valueOf(institutionKey.entityId))) + isEqualTo(Long.valueOf(institutionKey.modelId))) .build() .execute() .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java index ab4ae1a3..08575066 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/LmsAPIService.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms; +import java.io.InputStream; + import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.util.Result; @@ -17,8 +19,8 @@ public interface LmsAPIService { Result createLmsAPITemplate(LmsSetup lmsSetup); - Result createSEBStartConfiguration(Long lmsSetupId); + Result createSEBStartConfiguration(Long lmsSetupId); - Result createSEBStartConfiguration(LmsSetup lmsSetup); + Result createSEBStartConfiguration(LmsSetup lmsSetup); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java index bd9c5720..11e20088 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; +import java.io.InputStream; + import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; @@ -30,7 +32,7 @@ public class LmsAPIServiceImpl implements LmsAPIService { @Override public Result createLmsAPITemplate(final Long lmsSetupId) { return this.lmsSetupDAO - .byId(lmsSetupId) + .byPK(lmsSetupId) .flatMap(this::createLmsAPITemplate); } @@ -46,14 +48,14 @@ public class LmsAPIServiceImpl implements LmsAPIService { } @Override - public Result createSEBStartConfiguration(final Long lmsSetupId) { + public Result createSEBStartConfiguration(final Long lmsSetupId) { return this.lmsSetupDAO - .byId(lmsSetupId) + .byPK(lmsSetupId) .flatMap(this::createSEBStartConfiguration); } @Override - public Result createSEBStartConfiguration(final LmsSetup lmsSetup) { + public Result createSEBStartConfiguration(final LmsSetup lmsSetup) { // TODO implementation of creation of SEB start configuration for specified LmsSetup // A SEB start configuration should at least contain the SEB-Client-Credentials to access the SEB Server API diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java new file mode 100644 index 00000000..b25a6f81 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ActivatableEntityController.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.weblayer.api; + +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.EntityType; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; + +public abstract class ActivatableEntityController extends EntityController { + + public ActivatableEntityController( + final AuthorizationGrantService authorizationGrantService, + final BulkActionService bulkActionService, + final EntityDAO entityDAO) { + + super(authorizationGrantService, bulkActionService, entityDAO); + } + + @RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) + public EntityProcessingReport activate(@PathVariable final String id) { + return setActive(id, true) + .getOrThrow(); + } + + @RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) + public EntityProcessingReport deactivate(@PathVariable final String id) { + return setActive(id, false) + .getOrThrow(); + } + + @RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE) + public EntityProcessingReport delete(@PathVariable final String id) { + return deactivate(id); + } + + private Result setActive(final String id, final boolean active) { + final EntityType entityType = this.entityDAO.entityType(); + final BulkAction bulkAction = new BulkAction( + (active) ? Type.ACTIVATE : Type.DEACTIVATE, + entityType, + new EntityKey(id, entityType)); + + return this.entityDAO.byModelId(id) + .flatMap(entity -> this.authorizationGrantService.checkGrantOnEntity( + entity, + PrivilegeType.WRITE)) + .flatMap(entity -> this.bulkActionService.createReport(bulkAction)); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java new file mode 100644 index 00000000..ad9f6644 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.weblayer.api; + +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.EntityType; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO; + +public abstract class EntityController { + + protected final AuthorizationGrantService authorizationGrantService; + protected final BulkActionService bulkActionService; + protected final EntityDAO entityDAO; + + protected EntityController( + final AuthorizationGrantService authorizationGrantService, + final BulkActionService bulkActionService, + final EntityDAO entityDAO) { + + this.authorizationGrantService = authorizationGrantService; + this.bulkActionService = bulkActionService; + this.entityDAO = entityDAO; + } + + @RequestMapping(path = "/{id}", method = RequestMethod.GET) + public T accountInfo(@PathVariable final String id) { + return this.entityDAO + .byModelId(id) + .flatMap(entity -> this.authorizationGrantService.checkGrantOnEntity( + entity, + PrivilegeType.READ_ONLY)) + .getOrThrow(); + } + + @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE) + public EntityProcessingReport hardDeleteUser(@PathVariable final String id) { + final EntityType entityType = this.entityDAO.entityType(); + final BulkAction bulkAction = new BulkAction( + Type.HARD_DELETE, + entityType, + new EntityKey(id, entityType)); + + return this.entityDAO.byModelId(id) + .flatMap(entity -> this.authorizationGrantService.checkGrantOnEntity( + entity, + PrivilegeType.WRITE)) + .flatMap(entity -> this.bulkActionService.createReport(bulkAction)) + .getOrThrow(); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java new file mode 100644 index 00000000..3190869f --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAdministrationController.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.weblayer.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import javax.validation.Valid; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import ch.ethz.seb.sebserver.gbl.Constants; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; +import ch.ethz.seb.sebserver.gbl.model.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Page; +import ch.ethz.seb.sebserver.gbl.model.Page.SortOrder; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType; + +@WebServiceProfile +@RestController +@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_EXAM_ADMINISTRATION) +public class ExamAdministrationController { + + private final AuthorizationGrantService authorizationGrantService; + private final UserActivityLogDAO userActivityLogDAO; + private final ExamDAO examDAO; + private final PaginationService paginationService; + private final BulkActionService bulkActionService; + + public ExamAdministrationController( + final AuthorizationGrantService authorizationGrantService, + final UserActivityLogDAO userActivityLogDAO, + final ExamDAO examDAO, + final PaginationService paginationService, + final BulkActionService bulkActionService) { + + this.authorizationGrantService = authorizationGrantService; + this.userActivityLogDAO = userActivityLogDAO; + this.examDAO = examDAO; + this.paginationService = paginationService; + this.bulkActionService = bulkActionService; + } + + @InitBinder + public void initBinder(final WebDataBinder binder) throws Exception { + this.authorizationGrantService + .getUserService() + .addUsersInstitutionDefaultPropertySupport(binder); + } + + @RequestMapping(method = RequestMethod.GET) + public Collection getAll( + @RequestParam( + name = Exam.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = Exam.FILTER_ATTR_LMS_SETUP, required = false) final Long lmsSetupId, + @RequestParam(name = Exam.FILTER_ATTR_ACTIVE, required = false) final Boolean active, + @RequestParam(name = Exam.FILTER_ATTR_NAME, required = false) final String name, + @RequestParam(name = Exam.FILTER_ATTR_FROM, required = false) final String from, + @RequestParam(name = Exam.FILTER_ATTR_STATUS, required = false) final ExamStatus status, + @RequestParam(name = Exam.FILTER_ATTR_TYPE, required = false) final ExamType type, + @RequestParam(name = Exam.FILTER_ATTR_OWNER, required = false) final String owner) { + + checkBaseReadPrivilege(institutionId); + + this.paginationService.setDefaultLimit(ExamRecordDynamicSqlSupport.examRecord); + return getExams(institutionId, lmsSetupId, active, name, from, status, type, owner); + } + + @RequestMapping(path = "/page", method = RequestMethod.GET) + public Page getPage( + @RequestParam( + name = Exam.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = Exam.FILTER_ATTR_LMS_SETUP, required = false) final Long lmsSetupId, + @RequestParam(name = Exam.FILTER_ATTR_ACTIVE, required = false) final Boolean active, + @RequestParam(name = Exam.FILTER_ATTR_NAME, required = false) final String name, + @RequestParam(name = Exam.FILTER_ATTR_FROM, required = false) final String from, + @RequestParam(name = Exam.FILTER_ATTR_STATUS, required = false) final ExamStatus status, + @RequestParam(name = Exam.FILTER_ATTR_TYPE, required = false) final ExamType type, + @RequestParam(name = Exam.FILTER_ATTR_OWNER, required = false) final String owner, + @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, + @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, + @RequestParam(name = Page.ATTR_SORT_BY, required = false) final String sortBy, + @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { + + checkBaseReadPrivilege(institutionId); + + // NOTE: several attributes for sorting may be originated by the QuizData from LMS not by the database + // of the SEB Server. Therefore in the case we have no or the default sorting we can use the + // native PaginationService within MyBatis and SQL. For the other cases we need an in-line sorting and paging + if (StringUtils.isBlank(sortBy) || + this.paginationService.isNativeSortingSupported(ExamRecordDynamicSqlSupport.examRecord, sortBy)) { + + return this.paginationService.getPage( + pageNumber, + pageSize, + sortBy, + sortOrder, + ExamRecordDynamicSqlSupport.examRecord, + () -> getExams(institutionId, lmsSetupId, active, name, from, status, type, owner)); + + } else { + + final int pageNum = this.paginationService.getPageNumber(pageNumber); + final int pSize = this.paginationService.getPageSize(pageSize); + + final List exams = new ArrayList<>( + getExams(institutionId, lmsSetupId, active, name, from, status, type, owner)); + + if (!StringUtils.isBlank(sortBy)) { + if (sortBy.equals(QuizData.QUIZ_ATTR_NAME)) { + Collections.sort(exams, (exam1, exam2) -> exam1.name.compareTo(exam2.name)); + } + if (sortBy.equals(QuizData.FILTER_ATTR_START_TIME)) { + Collections.sort(exams, (exam1, exam2) -> exam1.startTime.compareTo(exam2.startTime)); + } + } + + if (SortOrder.DESCENDING == sortOrder) { + Collections.reverse(exams); + } + + return new Page<>( + exams.size() / pSize, + pageNum, + sortBy, + sortOrder, + exams.subList(pageNum * pSize, pageNum * pSize + pSize)); + } + } + + @RequestMapping(path = "/{id}", method = RequestMethod.GET) + public Exam byId(@PathVariable final Long id) { + return this.examDAO + .byPK(id) + .flatMap(exam -> this.authorizationGrantService.checkGrantOnEntity( + exam, + PrivilegeType.READ_ONLY)) + .getOrThrow(); + } + + @RequestMapping(path = "/save", method = RequestMethod.POST) + public Exam save(@Valid @RequestBody final Exam exam) { + return this.authorizationGrantService + .checkGrantOnEntity(exam, PrivilegeType.MODIFY) + .flatMap(this.examDAO::save) + .flatMap(entity -> this.userActivityLogDAO.log(ActivityType.MODIFY, entity)) + .getOrThrow(); + } + + @RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) + public EntityProcessingReport activate(@PathVariable final Long id) { + return setActive(id, true) + .getOrThrow(); + } + + @RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) + public EntityProcessingReport deactivate(@PathVariable final Long id) { + return setActive(id, false) + .getOrThrow(); + } + + private Result setActive(final Long id, final boolean active) { + + return this.bulkActionService.createReport(new BulkAction( + (active) ? Type.ACTIVATE : Type.DEACTIVATE, + EntityType.LMS_SETUP, + new EntityKey(id, EntityType.LMS_SETUP))); + + } + + private Collection getExams( + final Long institutionId, + final Long lmsSetupId, + final Boolean active, + final String name, + final String from, + final ExamStatus status, + final ExamType type, + final String owner) { + + this.authorizationGrantService.checkPrivilege( + EntityType.EXAM, + PrivilegeType.READ_ONLY, + institutionId); + + final DateTime _from = (from != null) + ? DateTime.parse(from, Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS) + : null; + + return this.examDAO.allMatching( + institutionId, + lmsSetupId, + name, + status, + type, + _from, + owner, + active).getOrThrow(); + } + + private void checkBaseReadPrivilege(final Long institutionId) { + this.authorizationGrantService.checkPrivilege( + EntityType.EXAM, + PrivilegeType.READ_ONLY, + institutionId); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java index d349aa7f..e533cbbb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/InstitutionController.java @@ -21,8 +21,8 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.institution.Institution; @@ -62,24 +62,18 @@ public class InstitutionController { @RequestMapping(path = "/self", method = RequestMethod.GET) public Institution getOwn() { - - checkBaseReadPrivilege(); - final SEBServerUser currentUser = this.authorizationGrantService .getUserService() .getCurrentUser(); - final Long institutionId = currentUser.institutionId(); - return this.institutionDAO.byId(institutionId).getOrThrow(); + final Long institutionId = currentUser.institutionId(); + return this.institutionDAO.byPK(institutionId).getOrThrow(); } @RequestMapping(path = "/{id}", method = RequestMethod.GET) public Institution getById(@PathVariable final Long id) { - - checkBaseReadPrivilege(); - return this.institutionDAO - .byId(id) + .byPK(id) .flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity( inst, PrivilegeType.READ_ONLY)) @@ -90,8 +84,6 @@ public class InstitutionController { public Collection getAll( @RequestParam(name = Institution.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { - checkBaseReadPrivilege(); - if (!this.authorizationGrantService.hasBasePrivilege( EntityType.INSTITUTION, PrivilegeType.READ_ONLY)) { @@ -126,13 +118,15 @@ public class InstitutionController { } @RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) - public Institution activate(@PathVariable final Long id) { - return setActive(id, true); + public EntityProcessingReport activate(@PathVariable final Long id) { + return setActive(id, true) + .getOrThrow(); } @RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) - public Institution deactivate(@PathVariable final Long id) { - return setActive(id, false); + public EntityProcessingReport deactivate(@PathVariable final Long id) { + return setActive(id, false) + .getOrThrow(); } @RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE) @@ -142,7 +136,8 @@ public class InstitutionController { return this.bulkActionService.createReport(new BulkAction( Type.DEACTIVATE, EntityType.INSTITUTION, - new EntityKey(id, EntityType.INSTITUTION))); + new EntityKey(id, EntityType.INSTITUTION))) + .getOrThrow(); } @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE) @@ -152,7 +147,8 @@ public class InstitutionController { return this.bulkActionService.createReport(new BulkAction( Type.HARD_DELETE, EntityType.INSTITUTION, - new EntityKey(id, EntityType.INSTITUTION))); + new EntityKey(id, EntityType.INSTITUTION))) + .getOrThrow(); } private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) { @@ -160,24 +156,21 @@ public class InstitutionController { EntityType.INSTITUTION, type); - this.institutionDAO.byId(id) + this.institutionDAO.byPK(id) .flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity( institution, type)) .getOrThrow(); } - private Institution setActive(final Long id, final boolean active) { + private Result setActive(final Long id, final boolean active) { checkPrivilegeForInstitution(id, PrivilegeType.MODIFY); - this.bulkActionService.doBulkAction(new BulkAction( + return this.bulkActionService.createReport(new BulkAction( (active) ? Type.ACTIVATE : Type.DEACTIVATE, EntityType.INSTITUTION, new EntityKey(id, EntityType.INSTITUTION))); - return this.institutionDAO - .byId(id) - .getOrThrow(); } private Result save(final Institution institution, final PrivilegeType privilegeType) { @@ -192,10 +185,4 @@ public class InstitutionController { .flatMap(inst -> this.userActivityLogDAO.log(activityType, inst)); } - private void checkBaseReadPrivilege() { - this.authorizationGrantService.checkHasAnyPrivilege( - EntityType.INSTITUTION, - PrivilegeType.READ_ONLY); - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java index b4de18f1..a0463498 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsSetupController.java @@ -8,12 +8,18 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.io.InputStream; import java.util.Collection; import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -30,9 +36,8 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; -import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; -import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; @@ -66,44 +71,42 @@ public class LmsSetupController { this.lmsAPIService = lmsAPIService; } + @InitBinder + public void initBinder(final WebDataBinder binder) throws Exception { + this.authorizationGrantService + .getUserService() + .addUsersInstitutionDefaultPropertySupport(binder); + } + @RequestMapping(method = RequestMethod.GET) public Collection getAll( - @RequestParam(name = LmsSetup.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId, + @RequestParam( + name = LmsSetup.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, @RequestParam(name = LmsSetup.FILTER_ATTR_NAME, required = false) final String name, @RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LmsType lmsType, @RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { - checkBaseReadPrivilege(); - - final SEBServerUser currentUser = this.authorizationGrantService - .getUserService() - .getCurrentUser(); - final Long usersInstitution = currentUser.institutionId(); - final Long instId = (institutionId != null) ? institutionId : usersInstitution; - - if (!this.authorizationGrantService.hasBasePrivilege( - EntityType.LMS_SETUP, - PrivilegeType.READ_ONLY) && - !instId.equals(usersInstitution)) { - - throw new PermissionDeniedException( - EntityType.LMS_SETUP, - PrivilegeType.READ_ONLY, - currentUser.getUserInfo().uuid); - } + checkReadPrivilege(institutionId); return this.lmsSetupDAO - .allMatching(instId, name, lmsType, active) + .allMatching(institutionId, name, lmsType, active) .getOrThrow(); } @RequestMapping(path = "/names", method = RequestMethod.GET) public Collection getNames( - @RequestParam(name = LmsSetup.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId, + @RequestParam( + name = LmsSetup.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, @RequestParam(name = LmsSetup.FILTER_ATTR_NAME, required = false) final String name, @RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LmsType lmsType, @RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { + checkReadPrivilege(institutionId); + return getAll(institutionId, name, lmsType, active) .stream() .map(LmsSetup::toName) @@ -112,13 +115,10 @@ public class LmsSetupController { @RequestMapping(path = "/{id}", method = RequestMethod.GET) public LmsSetup getById(@PathVariable final Long id) { - - checkBaseReadPrivilege(); - return this.lmsSetupDAO - .byId(id) - .flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity( - inst, + .byPK(id) + .flatMap(lmsSetup -> this.authorizationGrantService.checkGrantOnEntity( + lmsSetup, PrivilegeType.READ_ONLY)) .getOrThrow(); } @@ -127,15 +127,28 @@ public class LmsSetupController { path = "/create_seb_config/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) // TODO check if this is the right format - public byte[] createSEBConfig(@PathVariable final Long id) { + public void downloadSEBConfig( + @PathVariable final Long id, + final HttpServletResponse response) { this.authorizationGrantService.checkHasAnyPrivilege( EntityType.LMS_SETUP, PrivilegeType.WRITE); - return this.lmsAPIService - .createSEBStartConfiguration(id) - .getOrThrow(); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + response.setStatus(HttpStatus.OK.value()); + + try { + final InputStream sebConfigFileIn = this.lmsAPIService + .createSEBStartConfiguration(id) + .getOrThrow(); + + IOUtils.copyLarge(sebConfigFileIn, response.getOutputStream()); + response.flushBuffer(); + + } catch (final Exception e) { + throw new RuntimeException("Unexpected error while trying to creae SEB start config: ", e); + } } @RequestMapping(path = "/create", method = RequestMethod.PUT) @@ -151,12 +164,12 @@ public class LmsSetupController { } @RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) - public LmsSetup activate(@PathVariable final Long id) { + public EntityProcessingReport activate(@PathVariable final Long id) { return setActive(id, true); } @RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) - public LmsSetup deactivate(@PathVariable final Long id) { + public EntityProcessingReport deactivate(@PathVariable final Long id) { return setActive(id, false); } @@ -167,7 +180,8 @@ public class LmsSetupController { return this.bulkActionService.createReport(new BulkAction( Type.DEACTIVATE, EntityType.LMS_SETUP, - new EntityKey(id, EntityType.LMS_SETUP))); + new EntityKey(id, EntityType.LMS_SETUP))) + .getOrThrow(); } @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE) @@ -177,31 +191,29 @@ public class LmsSetupController { return this.bulkActionService.createReport(new BulkAction( Type.HARD_DELETE, EntityType.LMS_SETUP, - new EntityKey(id, EntityType.LMS_SETUP))); + new EntityKey(id, EntityType.LMS_SETUP))) + .getOrThrow(); } - private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) { + private void checkPrivilegeForInstitution(final Long lmsSetupId, final PrivilegeType type) { this.authorizationGrantService.checkHasAnyPrivilege( EntityType.LMS_SETUP, type); - this.lmsSetupDAO.byId(id) + this.lmsSetupDAO.byPK(lmsSetupId) .flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity( institution, type)) .getOrThrow(); } - private LmsSetup setActive(final Long id, final boolean active) { + private EntityProcessingReport setActive(final Long id, final boolean active) { checkPrivilegeForInstitution(id, PrivilegeType.MODIFY); - this.bulkActionService.doBulkAction(new BulkAction( + return this.bulkActionService.createReport(new BulkAction( (active) ? Type.ACTIVATE : Type.DEACTIVATE, EntityType.LMS_SETUP, - new EntityKey(id, EntityType.LMS_SETUP))); - - return this.lmsSetupDAO - .byId(id) + new EntityKey(id, EntityType.LMS_SETUP))) .getOrThrow(); } @@ -214,13 +226,14 @@ public class LmsSetupController { return this.authorizationGrantService .checkGrantOnEntity(lmsSetup, privilegeType) .flatMap(this.lmsSetupDAO::save) - .flatMap(inst -> this.userActivityLogDAO.log(activityType, inst)); + .flatMap(exam -> this.userActivityLogDAO.log(activityType, exam)); } - private void checkBaseReadPrivilege() { - this.authorizationGrantService.checkHasAnyPrivilege( + private void checkReadPrivilege(final Long institutionId) { + this.authorizationGrantService.checkPrivilege( EntityType.LMS_SETUP, - PrivilegeType.READ_ONLY); + PrivilegeType.READ_ONLY, + institutionId); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizImportController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizImportController.java index bccfb556..d08e4ca7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizImportController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/QuizImportController.java @@ -74,19 +74,20 @@ public class QuizImportController { @RequestParam(name = LMS_SETUP.ATTR_ID, required = true) final Long lmsSetupId, @RequestParam(name = QuizData.FILTER_ATTR_NAME, required = false) final String nameLike, @RequestParam(name = QuizData.FILTER_ATTR_START_TIME, required = false) final String startTime, - @RequestParam(name = QuizData.PAGE_ATTR_NUMBER, required = false) final Integer pageNumber, - @RequestParam(name = QuizData.PAGE_ATTR_SIZE, required = false) final Integer pageSize, - @RequestParam(name = QuizData.PAGE_ATTR_SORT_BY, required = false) final String orderBy, - @RequestParam(name = QuizData.PAGE_ATTR_SORT_ORDER, required = false) final String sortOrder) { - - this.authorizationGrantService.checkHasAnyPrivilege( - EntityType.EXAM, - PrivilegeType.READ_ONLY); + @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, + @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, + @RequestParam(name = Page.ATTR_SORT_BY, required = false) final String orderBy, + @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final String sortOrder) { final LmsAPITemplate lmsAPITemplate = this.lmsAPIService .createLmsAPITemplate(lmsSetupId) .getOrThrow(); + this.authorizationGrantService.checkPrivilege( + EntityType.EXAM, + PrivilegeType.READ_ONLY, + lmsAPITemplate.lmsSetup().institutionId); + return lmsAPITemplate.getQuizzesPage( nameLike, Utils.dateTimeStringToTimestamp(startTime, null), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/RestAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/RestAPI.java index 794631d0..4cffc9d2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/RestAPI.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/RestAPI.java @@ -18,6 +18,8 @@ public class RestAPI { public static final String ENDPOINT_QUIZ_IMPORT = "/quiz"; + public static final String ENDPOINT_EXAM_ADMINISTRATION = "/exam"; + public static final String ENDPOINT_USER_ACTIVITY_LOG = "/useractivity"; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java index 74077429..6c3d3236 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserAccountController.java @@ -13,15 +13,14 @@ import java.util.Collection; import javax.validation.Valid; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import ch.ethz.seb.sebserver.gbl.model.EntityKey; -import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.user.UserFilter; @@ -34,8 +33,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; -import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; -import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType; @@ -45,11 +42,10 @@ import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint; @WebServiceProfile @RestController @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACCOUNT) -public class UserAccountController { +public class UserAccountController extends ActivatableEntityController { private final UserDAO userDao; private final AuthorizationGrantService authorizationGrantService; - private final UserService userService; private final UserActivityLogDAO userActivityLogDAO; private final PaginationService paginationService; private final BulkActionService bulkActionService; @@ -58,34 +54,40 @@ public class UserAccountController { public UserAccountController( final UserDAO userDao, final AuthorizationGrantService authorizationGrantService, - final UserService userService, final UserActivityLogDAO userActivityLogDAO, final PaginationService paginationService, final BulkActionService bulkActionService, final ApplicationEventPublisher applicationEventPublisher) { + super(authorizationGrantService, bulkActionService, userDao); this.userDao = userDao; this.authorizationGrantService = authorizationGrantService; - this.userService = userService; this.userActivityLogDAO = userActivityLogDAO; this.paginationService = paginationService; this.bulkActionService = bulkActionService; this.applicationEventPublisher = applicationEventPublisher; } + @InitBinder + public void initBinder(final WebDataBinder binder) throws Exception { + this.authorizationGrantService + .getUserService() + .addUsersInstitutionDefaultPropertySupport(binder); + } + @RequestMapping(method = RequestMethod.GET) public Collection getAll( - @RequestParam(name = UserFilter.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId, + @RequestParam( + name = UserFilter.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, @RequestParam(name = UserFilter.FILTER_ATTR_ACTIVE, required = false) final Boolean active, @RequestParam(name = UserFilter.FILTER_ATTR_NAME, required = false) final String name, @RequestParam(name = UserFilter.FILTER_ATTR_USER_NAME, required = false) final String username, @RequestParam(name = UserFilter.FILTER_ATTR_EMAIL, required = false) final String email, @RequestParam(name = UserFilter.FILTER_ATTR_LOCALE, required = false) final String locale) { - // fist check if current user has any privileges for this action - this.authorizationGrantService.checkHasAnyPrivilege( - EntityType.USER, - PrivilegeType.READ_ONLY); + checkReadPrivilege(institutionId); this.paginationService.setDefaultLimit(UserRecordDynamicSqlSupport.userRecord); return getAll(createUserFilter(institutionId, active, name, username, email, locale)); @@ -93,7 +95,10 @@ public class UserAccountController { @RequestMapping(path = "/page", method = RequestMethod.GET) public Page getPage( - @RequestParam(name = UserFilter.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId, + @RequestParam( + name = UserFilter.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, @RequestParam(name = UserFilter.FILTER_ATTR_ACTIVE, required = false) final Boolean active, @RequestParam(name = UserFilter.FILTER_ATTR_NAME, required = false) final String name, @RequestParam(name = UserFilter.FILTER_ATTR_USER_NAME, required = false) final String username, @@ -104,10 +109,7 @@ public class UserAccountController { @RequestParam(name = Page.ATTR_SORT_BY, required = false) final String sortBy, @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { - // fist check if current user has any privileges for this action - this.authorizationGrantService.checkHasAnyPrivilege( - EntityType.USER, - PrivilegeType.READ_ONLY); + checkReadPrivilege(institutionId); return this.paginationService.getPage( pageNumber, @@ -121,21 +123,22 @@ public class UserAccountController { @RequestMapping(path = "/me", method = RequestMethod.GET) public UserInfo loggedInUser() { - return this.userService + return this.authorizationGrantService + .getUserService() .getCurrentUser() .getUserInfo(); } - @RequestMapping(path = "/{uuid}", method = RequestMethod.GET) - public UserInfo accountInfo(@PathVariable final String uuid) { - return this.userDao - .byUuid(uuid) - .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity( - userInfo, - PrivilegeType.READ_ONLY)) - .getOrThrow(); - - } +// @Override +// @RequestMapping(path = "/{uuid}", method = RequestMethod.GET) +// public UserInfo accountInfo(@PathVariable final String uuid) { +// return this.userDao +// .byModelId(uuid) +// .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity( +// userInfo, +// PrivilegeType.READ_ONLY)) +// .getOrThrow(); +// } @RequestMapping(path = "/create", method = RequestMethod.PUT) public UserInfo createUser(@Valid @RequestBody final UserMod userData) { @@ -150,60 +153,52 @@ public class UserAccountController { } - @RequestMapping(path = "/{uuid}/activate", method = RequestMethod.POST) - public UserInfo activateUser(@PathVariable final String uuid) { - return setActive(uuid, true); - } +// @Override +// @RequestMapping(path = "/{uuid}/activate", method = RequestMethod.POST) +// public EntityProcessingReport activateUser(@PathVariable final String uuid) { +// return setActive(uuid, true) +// .getOrThrow(); +// } +// +// @Override +// @RequestMapping(value = "/{uuid}/deactivate", method = RequestMethod.POST) +// public EntityProcessingReport deactivateUser(@PathVariable final String uuid) { +// return setActive(uuid, false) +// .getOrThrow(); +// } +// +// @Override +// @RequestMapping(path = "/{uuid}/delete", method = RequestMethod.DELETE) +// public EntityProcessingReport deleteUser(@PathVariable final String uuid) { +// checkPrivilegeForUser(uuid, PrivilegeType.WRITE); +// +// return this.bulkActionService.createReport(new BulkAction( +// Type.DEACTIVATE, +// EntityType.USER, +// new EntityKey(uuid, EntityType.USER, false))) +// .getOrThrow(); +// } - @RequestMapping(value = "/{uuid}/deactivate", method = RequestMethod.POST) - public UserInfo deactivateUser(@PathVariable final String uuid) { - return setActive(uuid, false); - } +// private void checkPrivilegeForUser(final String uuid, final PrivilegeType type) { +// this.authorizationGrantService.checkHasAnyPrivilege( +// EntityType.USER, +// type); +// +// this.userDao.byModelId(uuid) +// .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity( +// userInfo, +// type)) +// .getOrThrow(); +// } - @RequestMapping(path = "/{uuid}/delete", method = RequestMethod.DELETE) - public EntityProcessingReport deleteUser(@PathVariable final String uuid) { - checkPrivilegeForUser(uuid, PrivilegeType.WRITE); - - return this.bulkActionService.createReport(new BulkAction( - Type.DEACTIVATE, - EntityType.USER, - new EntityKey(uuid, EntityType.USER, false))); - } - - @RequestMapping(path = "/{uuid}/hard-delete", method = RequestMethod.DELETE) - public EntityProcessingReport hardDeleteUser(@PathVariable final String uuid) { - checkPrivilegeForUser(uuid, PrivilegeType.WRITE); - - return this.bulkActionService.createReport(new BulkAction( - Type.HARD_DELETE, - EntityType.USER, - new EntityKey(uuid, EntityType.USER, false))); - } - - private void checkPrivilegeForUser(final String uuid, final PrivilegeType type) { - this.authorizationGrantService.checkHasAnyPrivilege( - EntityType.USER, - type); - - this.userDao.byUuid(uuid) - .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity( - userInfo, - type)) - .getOrThrow(); - } - - private UserInfo setActive(final String uuid, final boolean active) { - this.checkPrivilegeForUser(uuid, PrivilegeType.MODIFY); - - this.bulkActionService.doBulkAction(new BulkAction( - (active) ? Type.ACTIVATE : Type.DEACTIVATE, - EntityType.USER, - new EntityKey(uuid, EntityType.USER, false))); - - return this.userDao - .byUuid(uuid) - .getOrThrow(); - } +// private Result setActive(final String uuid, final boolean active) { +// this.checkPrivilegeForUser(uuid, PrivilegeType.MODIFY); +// +// return this.bulkActionService.createReport(new BulkAction( +// (active) ? Type.ACTIVATE : Type.DEACTIVATE, +// EntityType.USER, +// new EntityKey(uuid, EntityType.USER, false))); +// } private Result _saveUser(final UserMod userData, final PrivilegeType privilegeType) { @@ -261,4 +256,11 @@ public class UserAccountController { : null; } + private void checkReadPrivilege(final Long institutionId) { + this.authorizationGrantService.checkPrivilege( + EntityType.USER, + PrivilegeType.READ_ONLY, + institutionId); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserActivityLogController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserActivityLogController.java index 4ad78d54..3051af92 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserActivityLogController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/UserActivityLogController.java @@ -13,8 +13,11 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.function.Predicate; import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -26,11 +29,14 @@ import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserActivityLogRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.UserActivityLogRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; @WebServiceProfile @@ -52,84 +58,113 @@ public class UserActivityLogController { this.paginationService = paginationService; } + @InitBinder + public void initBinder(final WebDataBinder binder) throws Exception { + this.authorizationGrantService + .getUserService() + .addUsersInstitutionDefaultPropertySupport(binder); + } + @RequestMapping(method = RequestMethod.GET) public Collection getAll( - @RequestParam(required = false) final Long from, - @RequestParam(required = false) final Long to, - @RequestParam(required = false) final String activityTypes, - @RequestParam(required = false) final String entityTypes) { + @RequestParam( + name = UserActivityLog.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam( + name = UserActivityLog.FILTER_ATTR_FROM, + required = false) final String from, + @RequestParam( + name = UserActivityLog.FILTER_ATTR_TO, + required = false) final String to, + @RequestParam( + name = UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES, + required = false) final String activityTypes, + @RequestParam( + name = UserActivityLog.FILTER_ATTR_ENTITY_TYPES, + required = false) final String entityTypes) { - checkBaseReadPrivilege(); + checkBaseReadPrivilege(institutionId); this.paginationService.setDefaultLimit(UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord); - return _getAll(null, from, to, activityTypes, entityTypes); + return _getAll(institutionId, null, from, to, activityTypes, entityTypes); } @RequestMapping(path = "/{userId}", method = RequestMethod.GET) public Collection getAllForUser( @PathVariable final String userId, - @RequestParam(required = false) final Long from, - @RequestParam(required = false) final Long to, - @RequestParam(required = false) final String activityTypes, - @RequestParam(required = false) final String entityTypes) { + @RequestParam( + name = UserActivityLog.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = UserActivityLog.FILTER_ATTR_FROM, required = false) final String from, + @RequestParam(name = UserActivityLog.FILTER_ATTR_TO, required = false) final String to, + @RequestParam(name = UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES, + required = false) final String activityTypes, + @RequestParam(name = UserActivityLog.FILTER_ATTR_ENTITY_TYPES, required = false) final String entityTypes) { - checkBaseReadPrivilege(); + checkBaseReadPrivilege(institutionId); this.paginationService.setDefaultLimit(UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord); - return _getAll(userId, from, to, activityTypes, entityTypes); + return _getAll(institutionId, userId, from, to, activityTypes, entityTypes); } @RequestMapping(path = "/page", method = RequestMethod.GET) public Page getPage( - @RequestParam(required = false) final Long from, - @RequestParam(required = false) final Long to, - @RequestParam(required = false) final String activityTypes, - @RequestParam(required = false) final String entityTypes, + @RequestParam( + name = UserActivityLog.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = UserActivityLog.FILTER_ATTR_FROM, required = false) final String from, + @RequestParam(name = UserActivityLog.FILTER_ATTR_TO, required = false) final String to, + @RequestParam(name = UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES, + required = false) final String activityTypes, + @RequestParam(name = UserActivityLog.FILTER_ATTR_ENTITY_TYPES, required = false) final String entityTypes, @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, @RequestParam(name = Page.ATTR_SORT_BY, required = false) final String sortBy, @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { - checkBaseReadPrivilege(); + checkBaseReadPrivilege(institutionId); return this.paginationService.getPage( pageNumber, pageSize, sortBy, sortOrder, UserRecordDynamicSqlSupport.userRecord, - () -> _getAll(null, from, to, activityTypes, entityTypes)); + () -> _getAll(institutionId, null, from, to, activityTypes, entityTypes)); } @RequestMapping(path = "/page/{userId}", method = RequestMethod.GET) public Page getPageForUser( @PathVariable final String userId, - @RequestParam(required = false) final Long from, - @RequestParam(required = false) final Long to, - @RequestParam(required = false) final String activityTypes, - @RequestParam(required = false) final String entityTypes, + @RequestParam( + name = UserActivityLog.FILTER_ATTR_INSTITUTION, + required = true, + defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, + @RequestParam(name = UserActivityLog.FILTER_ATTR_FROM, required = false) final String from, + @RequestParam(name = UserActivityLog.FILTER_ATTR_TO, required = false) final String to, + @RequestParam(name = UserActivityLog.FILTER_ATTR_ACTIVITY_TYPES, + required = false) final String activityTypes, + @RequestParam(name = UserActivityLog.FILTER_ATTR_ENTITY_TYPES, required = false) final String entityTypes, @RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber, @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, @RequestParam(name = Page.ATTR_SORT_BY, required = false) final String sortBy, @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { - checkBaseReadPrivilege(); + checkBaseReadPrivilege(institutionId); return this.paginationService.getPage( pageNumber, pageSize, sortBy, sortOrder, UserRecordDynamicSqlSupport.userRecord, - () -> _getAll(userId, from, to, activityTypes, entityTypes)); - } - - private void checkBaseReadPrivilege() { - this.authorizationGrantService.checkHasAnyPrivilege( - EntityType.USER_ACTIVITY_LOG, - PrivilegeType.READ_ONLY); + () -> _getAll(institutionId, userId, from, to, activityTypes, entityTypes)); } private Collection _getAll( + final Long institutionId, final String userId, - final Long from, - final Long to, + final String from, + final String to, final String activityTypes, final String entityTypes) { @@ -142,24 +177,32 @@ public class UserActivityLogController { Arrays.asList(StringUtils.split(entityTypes, Constants.LIST_SEPARATOR)))) : null; - if (_activityTypes != null || _entityTypes != null) { + final Predicate filter = (_activityTypes != null || _entityTypes != null) + ? record -> { + if (_activityTypes != null && !_activityTypes.contains(record.getActivityType())) { + return false; + } + if (_entityTypes != null && !_entityTypes.contains(record.getEntityType())) { + return false; + } - return this.userActivityLogDAO.all(userId, from, to, record -> { - if (_activityTypes != null && !_activityTypes.contains(record.getActivityType())) { - return false; - } - if (_entityTypes != null && !_entityTypes.contains(record.getEntityType())) { - return false; + return true; } + : record -> true; - return true; - }).getOrThrow(); + return this.userActivityLogDAO.all( + institutionId, + userId, + Utils.toMilliSeconds(from), + Utils.toMilliSeconds(to), + filter).getOrThrow(); + } - } else { - - return this.userActivityLogDAO.all(userId, from, to, record -> true) - .getOrThrow(); - } + private void checkBaseReadPrivilege(final Long institutionId) { + this.authorizationGrantService.checkPrivilege( + EntityType.USER_ACTIVITY_LOG, + PrivilegeType.READ_ONLY, + institutionId); } } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/AdministrationAPIIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/AdministrationAPIIntegrationTest.java index 7f741385..23276939 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/AdministrationAPIIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/AdministrationAPIIntegrationTest.java @@ -99,4 +99,12 @@ public abstract class AdministrationAPIIntegrationTest { return obtainAccessToken("examAdmin1", "admin"); } +// protected static class TestHelper { +// +// private Supplier accessTokenSuplier; +// private String query; +// private String endpoint; +// private Object +// } + } diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserAPITest.java index f086b3e9..ae16bd51 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserAPITest.java @@ -21,6 +21,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.junit.Test; import org.springframework.http.MediaType; @@ -28,7 +29,10 @@ import org.springframework.test.context.jdbc.Sql; import com.fasterxml.jackson.core.type.TypeReference; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.APIMessage; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; @@ -113,6 +117,15 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { contentAsString); } + @Test + public void institutionalAdminNotAllowedToSeeUsersOfOtherInstitution() throws Exception { + final String token = getAdminInstitution1Access(); + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?institution=2") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isForbidden()) + .andReturn().getResponse().getContentAsString(); + } + @Test public void getAllUserInfoNoFilter() throws Exception { String token = getSebAdminAccess(); @@ -126,34 +139,65 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { // expecting all users for a SEBAdmin except inactive. assertNotNull(userInfos); - assertTrue(userInfos.size() == 7); + assertTrue(userInfos.size() == 3); assertNotNull(getUserInfo("admin", userInfos)); assertNotNull(getUserInfo("inst1Admin", userInfos)); assertNotNull(getUserInfo("examSupporter", userInfos)); - assertNotNull(getUserInfo("inst2Admin", userInfos)); - assertNotNull(getUserInfo("examAdmin1", userInfos)); - assertNotNull(getUserInfo("user1", userInfos)); - assertNotNull(getUserInfo("deactivatedUser", userInfos)); - token = getAdminInstitution1Access(); + token = getAdminInstitution2Access(); userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT) + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?institution=2") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + // expecting all users of institution 2 also inactive when active flag is not set + assertNotNull(userInfos); + assertTrue(userInfos.size() == 4); + assertNotNull(getUserInfo("inst2Admin", userInfos)); + assertNotNull(getUserInfo("examAdmin1", userInfos)); + assertNotNull(getUserInfo("deactivatedUser", userInfos)); + assertNotNull(getUserInfo("user1", userInfos)); + + //.. and without inactive, if active flag is set to true + token = getAdminInstitution2Access(); + userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?institution=2&active=true") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), new TypeReference>() { }); - // expecting all users of institution 1 for Institutional Admin of institution 1 assertNotNull(userInfos); assertTrue(userInfos.size() == 3); - assertNotNull(getUserInfo("admin", userInfos)); - assertNotNull(getUserInfo("inst1Admin", userInfos)); - assertNotNull(getUserInfo("examSupporter", userInfos)); + assertNotNull(getUserInfo("inst2Admin", userInfos)); + assertNotNull(getUserInfo("examAdmin1", userInfos)); + assertNotNull(getUserInfo("user1", userInfos)); + + //.. and only inactive, if active flag is set to false + token = getAdminInstitution2Access(); + userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?institution=2&active=false") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.size() == 1); + assertNotNull(getUserInfo("deactivatedUser", userInfos)); + } @Test public void getPageNoFilterNoPageAttributes() throws Exception { + + // expecting all user accounts of the institution of SEBAdmin + final String token = getSebAdminAccess(); final Page userInfos = this.jsonMapper.readValue( this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page") @@ -166,15 +210,18 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { assertNotNull(userInfos); assertTrue(userInfos.numberOfPages == 1); assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 7); - assertEquals("[user1, user2, user3, user4, user5, user6, user7]", getOrderedUUIDs(userInfos.content)); + assertTrue(userInfos.content.size() == 3); + assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content)); } @Test - public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception { + public void getPageNoFilterNoPageAttributesFromOtherInstitution() throws Exception { + + // expecting all user accounts of institution 2 + final String token = getSebAdminAccess(); final Page userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page?sortOrder=DESCENDING") + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page?institution=2") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -184,8 +231,26 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { assertNotNull(userInfos); assertTrue(userInfos.numberOfPages == 1); assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 7); - assertEquals("[user7, user6, user5, user4, user3, user2, user1]", getOrderedUUIDs(userInfos.content)); + assertTrue(userInfos.content.size() == 4); + assertEquals("[user3, user4, user6, user7]", getOrderedUUIDs(userInfos.content)); + } + + @Test + public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception { + final String token = getSebAdminAccess(); + final Page userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page?sort_order=DESCENDING") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(userInfos); + assertTrue(userInfos.numberOfPages == 1); + assertNotNull(userInfos.content); + assertTrue(userInfos.content.size() == 3); + assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content)); } @Test @@ -195,24 +260,25 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { // first page default sort order Page userInfos = this.jsonMapper.readValue( this.mockMvc - .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page?pageNumber=1&pageSize=3") - .header("Authorization", "Bearer " + token)) + .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + + "/page?page_number=1&page_size=3&institution=2") + .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), new TypeReference>() { }); assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 3); + assertTrue(userInfos.numberOfPages == 2); assertNotNull(userInfos.content); assertTrue(userInfos.content.size() == 3); - assertEquals("[user1, user2, user3]", getOrderedUUIDs(userInfos.content)); + assertEquals("[user3, user4, user6]", getOrderedUUIDs(userInfos.content)); // second page default sort order userInfos = this.jsonMapper.readValue( this.mockMvc .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT - + "/page?pageNumber=2&pageSize=3") + + "/page?page_number=2&page_size=3&institution=2") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -220,16 +286,17 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 3); + assertTrue(userInfos.numberOfPages == 2); assertNotNull(userInfos.content); - assertTrue(userInfos.content.size() == 3); - assertEquals("[user4, user5, user6]", getOrderedUUIDs(userInfos.content)); + assertTrue(userInfos.pageSize == 1); + assertTrue(userInfos.content.size() == 1); + assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); - // third page default sort order + // invalid page number should refer to last page userInfos = this.jsonMapper.readValue( this.mockMvc .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT - + "/page?pageNumber=3&pageSize=3") + + "/page?page_number=3&page_size=3&institution=2") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -237,7 +304,8 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 3); + assertTrue(userInfos.numberOfPages == 2); + assertTrue(userInfos.pageNumber == 2); assertNotNull(userInfos.content); assertTrue(userInfos.content.size() == 1); assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); @@ -246,7 +314,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { userInfos = this.jsonMapper.readValue( this.mockMvc .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT - + "/page?pageNumber=1&pageSize=3&sortOrder=DESCENDING") + + "/page?page_number=1&page_size=3&sort_order=DESCENDING&institution=2") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -254,10 +322,10 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(userInfos); - assertTrue(userInfos.numberOfPages == 3); + assertTrue(userInfos.numberOfPages == 2); assertNotNull(userInfos.content); assertTrue(userInfos.content.size() == 3); - assertEquals("[user7, user6, user5]", getOrderedUUIDs(userInfos.content)); + assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content)); } private String getOrderedUUIDs(final Collection list) { @@ -280,15 +348,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(userInfos); - assertTrue(userInfos.size() == 7); - assertNotNull(getUserInfo("deactivatedUser", userInfos)); + assertTrue(userInfos.size() == 3); } @Test - public void getAllUserInfoWithOnlyActice() throws Exception { + public void getAllUserInfoWithOnlyActive() throws Exception { final String token = getSebAdminAccess(); final List userInfos = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=true") + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=true&institution=2") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -296,14 +363,16 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(userInfos); - assertTrue(userInfos.size() == 6); + assertTrue(userInfos.size() == 3); assertNull(getUserInfo("deactivatedUser", userInfos)); } @Test public void getAllUserInfoOnlyInactive() throws Exception { + + // expecting none for SEBAdmins institution final String token = getSebAdminAccess(); - final List userInfos = this.jsonMapper.readValue( + List userInfos = this.jsonMapper.readValue( this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) @@ -311,6 +380,18 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { new TypeReference>() { }); + assertNotNull(userInfos); + assertTrue(userInfos.size() == 0); + + // expecting one for institution 2 + userInfos = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false&institution=2") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + assertNotNull(userInfos); assertTrue(userInfos.size() == 1); assertNotNull(getUserInfo("deactivatedUser", userInfos)); @@ -328,8 +409,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(userInfos); - assertTrue(userInfos.size() == 2); - assertNotNull(getUserInfo("examAdmin1", userInfos)); + assertTrue(userInfos.size() == 1); assertNotNull(getUserInfo("examSupporter", userInfos)); } @@ -381,8 +461,9 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { // check user activity log for newly created user final List logs = this.jsonMapper.readValue( this.mockMvc - .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user1?activityTypes=CREATE") - .header("Authorization", "Bearer " + token)) + .perform( + get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user1?activity_types=CREATE") + .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), new TypeReference>() { @@ -700,7 +781,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { @Test public void deactivateUserAccount() throws Exception { - final long timeNow = System.currentTimeMillis(); + final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account final String examAdminToken = getExamAdmin1(); this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/deactivate") @@ -709,16 +790,32 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { // With SEB Administrator it should work final String sebAdminToken = getSebAdminAccess(); - final UserInfo deactivatedUser = this.jsonMapper.readValue( + final EntityProcessingReport report = this.jsonMapper.readValue( this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/deactivate") .header("Authorization", "Bearer " + sebAdminToken)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(report); + assertNotNull(report.source); + assertTrue(report.dependencies.isEmpty()); // TODO + assertTrue(report.errors.isEmpty()); + assertTrue(report.source.size() == 1); + + // get user and check activity + final EntityKey key = report.source.iterator().next(); + final UserInfo user = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + key.modelId) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), new TypeReference() { }); - assertNotNull(deactivatedUser); - assertFalse(deactivatedUser.isActive()); + assertNotNull(user); + assertFalse(user.isActive()); // check also user activity log final Collection userLogs = this.jsonMapper.readValue( @@ -738,7 +835,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { @Test public void activateUserAccount() throws Exception { - final long timeNow = System.currentTimeMillis(); + final String timeNow = DateTime.now(DateTimeZone.UTC).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account final String examAdminToken = getExamAdmin1(); this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/activate") @@ -747,16 +844,32 @@ public class UserAPITest extends AdministrationAPIIntegrationTest { // With SEB Administrator it should work final String sebAdminToken = getSebAdminAccess(); - final UserInfo activatedUser = this.jsonMapper.readValue( + final EntityProcessingReport report = this.jsonMapper.readValue( this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/activate") .header("Authorization", "Bearer " + sebAdminToken)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), + new TypeReference() { + }); + + assertNotNull(report); + assertNotNull(report.source); + assertTrue(report.dependencies.isEmpty()); // TODO + assertTrue(report.errors.isEmpty()); + assertTrue(report.source.size() == 1); + + // get user and check activity + final EntityKey key = report.source.iterator().next(); + final UserInfo user = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/" + key.modelId) + .header("Authorization", "Bearer " + sebAdminToken)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), new TypeReference() { }); - assertNotNull(activatedUser); - assertTrue(activatedUser.isActive()); + assertNotNull(user); + assertTrue(user.isActive()); // check also user activity log final Collection userLogs = this.jsonMapper.readValue( diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserActivityLogAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserActivityLogAPITest.java index 0b1aed34..ba47a9ff 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserActivityLogAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/UserActivityLogAPITest.java @@ -8,18 +8,19 @@ package ch.ethz.seb.sebserver.webservice.integration.api; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; +import org.joda.time.DateTime; import org.junit.Test; import org.springframework.test.context.jdbc.Sql; import com.fasterxml.jackson.core.type.TypeReference; +import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI; @@ -38,14 +39,15 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(logs); - assertTrue(5 == logs.size()); + assertTrue(2 == logs.size()); } @Test public void getAllAsSEBAdminForUser() throws Exception { final String token = getSebAdminAccess(); + // for a user in another institution, the institution has to be defined List logs = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user4") + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user4?institution=2") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -55,6 +57,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { assertNotNull(logs); assertTrue(2 == logs.size()); + // for a user in the same institution no institution is needed logs = this.jsonMapper.readValue( this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user2") .header("Authorization", "Bearer " + token)) @@ -69,47 +72,18 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { @Test public void getAllAsSEBAdminInTimeRange() throws Exception { + final DateTime zeroDate = DateTime.parse("1970-01-01 00:00:00", Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); + assertEquals("0", String.valueOf(zeroDate.getMillis())); + final String sec2 = zeroDate.plus(1000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); + final String sec4 = zeroDate.plus(4000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); + final String sec5 = zeroDate.plus(5000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); + final String sec6 = zeroDate.plus(6000).toString(Constants.DATE_TIME_PATTERN_UTC_NO_MILLIS); + final String token = getSebAdminAccess(); List logs = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?from=2") - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(logs); - assertTrue(4 == logs.size()); - - logs = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?from=2&to=3") - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(logs); - assertTrue(1 == logs.size()); - - logs = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?from=2&to=4") - .header("Authorization", "Bearer " + token)) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(), - new TypeReference>() { - }); - - assertNotNull(logs); - assertTrue(2 == logs.size()); - } - - @Test - public void getAllAsSEBAdminForActivityType() throws Exception { - final String token = getSebAdminAccess(); - List logs = this.jsonMapper.readValue( - this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activityTypes=CREATE") - .header("Authorization", "Bearer " + token)) + this.mockMvc.perform( + get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institution=2&from=" + sec2) + .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), new TypeReference>() { @@ -120,8 +94,8 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { logs = this.jsonMapper.readValue( this.mockMvc - .perform( - get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activityTypes=CREATE,MODIFY") + .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institution=2&from=" + + sec2 + "&to=" + sec4) .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -129,7 +103,77 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(logs); - assertTrue(5 == logs.size()); + assertTrue(1 == logs.size()); + + logs = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institution=2&from=" + sec2 + + "&to=" + sec5) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(2 == logs.size()); + + logs = this.jsonMapper.readValue( + this.mockMvc + .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institution=2&from=" + sec2 + + "&to=" + sec6) + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(3 == logs.size()); + } + + @Test + public void getAllAsSEBAdminForActivityType() throws Exception { + final String token = getSebAdminAccess(); + List logs = this.jsonMapper.readValue( + this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activity_types=CREATE") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(1 == logs.size()); + + logs = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + + "?activity_types=CREATE,MODIFY") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(2 == logs.size()); + + // for other institution (2) + logs = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + + "?institution=2&activity_types=CREATE,MODIFY") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(3 == logs.size()); } @Test @@ -137,7 +181,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { final String token = getSebAdminAccess(); List logs = this.jsonMapper.readValue( this.mockMvc - .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?entityTypes=INSTITUTION") + .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?entity_types=INSTITUTION") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -151,7 +195,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { this.mockMvc .perform( get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG - + "?entityTypes=INSTITUTION,EXAM") + + "?entity_types=INSTITUTION,EXAM") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(), @@ -159,7 +203,21 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest { }); assertNotNull(logs); - assertTrue(5 == logs.size()); + assertTrue(2 == logs.size()); + + logs = this.jsonMapper.readValue( + this.mockMvc + .perform( + get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + + "?entity_types=INSTITUTION,EXAM&institution=2") + .header("Authorization", "Bearer " + token)) + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsString(), + new TypeReference>() { + }); + + assertNotNull(logs); + assertTrue(3 == logs.size()); } @Test diff --git a/src/test/resources/data-test.sql b/src/test/resources/data-test.sql index 69a3e22e..9727c0a5 100644 --- a/src/test/resources/data-test.sql +++ b/src/test/resources/data-test.sql @@ -24,10 +24,10 @@ INSERT INTO user_role VALUES ; INSERT INTO user_activity_log VALUES - (1, 'user1', 1, 'MODIFY', 'INSTITUTION', '1', 'some message'), - (2, 'user2', 2, 'CREATE', 'EXAM', '1', 'some message'), - (3, 'user3', 3, 'CREATE', 'EXAM', '2', 'some message'), - (4, 'user4', 4, 'CREATE', 'EXAM', '33', 'some message'), - (5, 'user4', 5, 'MODIFY', 'EXAM', '33', 'some message') + (1, 'user1', 1000, 'MODIFY', 'INSTITUTION', '1', 'some message'), + (2, 'user2', 2000, 'CREATE', 'EXAM', '1', 'some message'), + (3, 'user3', 3000, 'CREATE', 'EXAM', '2', 'some message'), + (4, 'user4', 4000, 'CREATE', 'EXAM', '33', 'some message'), + (5, 'user4', 5000, 'MODIFY', 'EXAM', '33', 'some message') ;