created controller abstraction for Entity and ActivatableEntity

This commit is contained in:
anhefti 2019-01-20 16:28:55 +01:00
parent 2f8b796b86
commit ca20785400
39 changed files with 1376 additions and 572 deletions

View file

@ -10,6 +10,9 @@ package ch.ethz.seb.sebserver.gbl.model;
public interface Entity extends ModelIdAware { public interface Entity extends ModelIdAware {
public static final String ATTR_ID = "id";
public static final String ATTR_INSTITUTION = "institution";
EntityType entityType(); EntityType entityType();
String getName(); String getName();

View file

@ -8,35 +8,40 @@
package ch.ethz.seb.sebserver.gbl.model; 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 class EntityKey {
public final String entityId; @JsonProperty(value = "modelId", required = true)
public final String modelId;
@JsonProperty(value = "entityType", required = true)
public final EntityType entityType; public final EntityType entityType;
@JsonIgnore
public final boolean isIdPK; public final boolean isIdPK;
@JsonCreator
public EntityKey( public EntityKey(
@NotNull final Long entityId, @JsonProperty(value = "modelId", required = true) final String modelId,
@NotNull final EntityType entityType) { @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.entityType = entityType;
this.isIdPK = true; this.isIdPK = true;
} }
public EntityKey( public String getModelId() {
@NotNull final String entityId, return this.modelId;
@NotNull final EntityType entityType,
final boolean isIdPK) {
this.entityId = entityId;
this.entityType = entityType;
this.isIdPK = isIdPK;
}
public String getEntityId() {
return this.entityId;
} }
public EntityType getEntityType() { public EntityType getEntityType() {
@ -47,8 +52,8 @@ public class EntityKey {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; 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.entityType == null) ? 0 : this.entityType.hashCode());
result = prime * result + ((this.modelId == null) ? 0 : this.modelId.hashCode());
return result; return result;
} }
@ -61,19 +66,19 @@ public class EntityKey {
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
final EntityKey other = (EntityKey) obj; 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) if (this.entityType != other.entityType)
return false; return false;
if (this.modelId == null) {
if (other.modelId != null)
return false;
} else if (!this.modelId.equals(other.modelId))
return false;
return true; return true;
} }
@Override @Override
public String toString() { public String toString() {
return "EntityKey [entityId=" + this.entityId + ", entityType=" + this.entityType + "]"; return "EntityKey [modelId=" + this.modelId + ", entityType=" + this.entityType + "]";
} }
} }

View file

@ -19,7 +19,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
@JsonProperty(value = "entityType", required = true) @JsonProperty(value = "entityType", required = true)
public final EntityType entityType; public final EntityType entityType;
@JsonProperty(value = Domain.ATTR_ID, required = true) @JsonProperty(value = Domain.ATTR_ID, required = true)
public final String id; public final String modelId;
@JsonProperty(value = "name", required = true) @JsonProperty(value = "name", required = true)
public final String name; public final String name;
@ -30,14 +30,14 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
@JsonProperty(value = "name", required = true) final String name) { @JsonProperty(value = "name", required = true) final String name) {
this.entityType = entityType; this.entityType = entityType;
this.id = id; this.modelId = id;
this.name = name; this.name = name;
} }
public EntityKeyAndName(final EntityKey entityKey, final String name) { public EntityKeyAndName(final EntityKey entityKey, final String name) {
this.entityType = entityKey.entityType; this.entityType = entityKey.entityType;
this.id = entityKey.entityId; this.modelId = entityKey.modelId;
this.name = name; this.name = name;
} }
@ -45,10 +45,6 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
return this.entityType; return this.entityType;
} }
public String getId() {
return this.id;
}
@Override @Override
public String getName() { public String getName() {
return this.name; return this.name;
@ -57,7 +53,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
@Override @Override
@JsonIgnore @JsonIgnore
public String getModelId() { public String getModelId() {
return this.id; return this.modelId;
} }
@Override @Override
@ -65,7 +61,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((this.entityType == null) ? 0 : this.entityType.hashCode()); 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()); result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
return result; return result;
} }
@ -81,10 +77,10 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
final EntityKeyAndName other = (EntityKeyAndName) obj; final EntityKeyAndName other = (EntityKeyAndName) obj;
if (this.entityType != other.entityType) if (this.entityType != other.entityType)
return false; return false;
if (this.id == null) { if (this.modelId == null) {
if (other.id != null) if (other.modelId != null)
return false; return false;
} else if (!this.id.equals(other.id)) } else if (!this.modelId.equals(other.modelId))
return false; return false;
if (this.name == null) { if (this.name == null) {
if (other.name != null) if (other.name != null)
@ -96,7 +92,7 @@ public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
@Override @Override
public String toString() { 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 + "]";
} }
} }

View file

@ -9,30 +9,46 @@
package ch.ethz.seb.sebserver.gbl.model; package ch.ethz.seb.sebserver.gbl.model;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.util.Utils;
public class EntityProcessingReport { public class EntityProcessingReport {
@JsonProperty(value = "source", required = true) @JsonProperty(value = "source", required = true)
public final Collection<Entity> source; public final Collection<EntityKey> source;
@JsonProperty(value = "dependencies", required = true) @JsonProperty(value = "dependencies", required = true)
public final Collection<EntityKeyAndName> dependencies; public final Collection<EntityKey> dependencies;
@JsonProperty(value = "errors", required = true) @JsonProperty(value = "errors", required = true)
public final Map<EntityKeyAndName, String> errors; public final Collection<ErrorEntry> errors;
@JsonCreator @JsonCreator
public EntityProcessingReport( public EntityProcessingReport(
@JsonProperty(value = "source", required = true) final Collection<Entity> source, @JsonProperty(value = "source", required = true) final Collection<EntityKey> source,
@JsonProperty(value = "dependencies", required = true) final Collection<EntityKeyAndName> dependencies, @JsonProperty(value = "dependencies", required = true) final Collection<EntityKey> dependencies,
@JsonProperty(value = "errors", required = true) final Map<EntityKeyAndName, String> errors) { @JsonProperty(value = "errors", required = true) final Collection<ErrorEntry> 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);
} }
} }

View file

@ -22,11 +22,11 @@ public final class Page<T> {
DESCENDING DESCENDING
} }
public static final String ATTR_NUMBER_OF_PAGES = "numberOfPages"; public static final String ATTR_NUMBER_OF_PAGES = "number_of_pages";
public static final String ATTR_PAGE_NUMBER = "pageNumber"; public static final String ATTR_PAGE_NUMBER = "page_number";
public static final String ATTR_PAGE_SIZE = "pageSize"; public static final String ATTR_PAGE_SIZE = "page_size";
public static final String ATTR_SORT_BY = "sortBy"; public static final String ATTR_SORT_BY = "sort_by";
public static final String ATTR_SORT_ORDER = "sortOrder"; public static final String ATTR_SORT_ORDER = "sort_order";
public static final String ATTR_CONTENT = "content"; public static final String ATTR_CONTENT = "content";
@JsonProperty(ATTR_NUMBER_OF_PAGES) @JsonProperty(ATTR_NUMBER_OF_PAGES)

View file

@ -26,7 +26,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
public final class Exam implements GrantEntity, Activatable { 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_LMS_SETUP = "lms_setup";
public static final String FILTER_ATTR_NAME = "name_like"; public static final String FILTER_ATTR_NAME = "name_like";
public static final String FILTER_ATTR_STATUS = "status"; public static final String FILTER_ATTR_STATUS = "status";

View file

@ -22,11 +22,6 @@ public final class QuizData {
public static final String FILTER_ATTR_NAME = "name_like"; public static final String FILTER_ATTR_NAME = "name_like";
public static final String FILTER_ATTR_START_TIME = "start_timestamp"; 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_ID = "quiz_id";
public static final String QUIZ_ATTR_NAME = "quiz_name"; public static final String QUIZ_ATTR_NAME = "quiz_name";
public static final String QUIZ_ATTR_DESCRIPTION = "quiz_description"; public static final String QUIZ_ATTR_DESCRIPTION = "quiz_description";

View file

@ -19,6 +19,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.Acti
public class UserActivityLog implements Entity { 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 @JsonIgnore
public final Long id; public final Long id;
@JsonProperty(USER_ACTIVITY_LOG.ATTR_USER_UUID) @JsonProperty(USER_ACTIVITY_LOG.ATTR_USER_UUID)

View file

@ -18,6 +18,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
@ -105,4 +106,24 @@ public final class Utils {
return map; 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();
}
} }

View file

@ -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.Entity;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; 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.UserActivityLogRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
@ -50,6 +51,25 @@ public class PaginationService {
initSortColumnMapping(); 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<String, String> tableMap = this.sortColumnMapping.get(table.name());
if (tableMap == null) {
return false;
}
return tableMap.containsKey(orderBy);
}
public void setDefaultLimitOfNotSet(final SqlTable table) { public void setDefaultLimitOfNotSet(final SqlTable table) {
if (PageHelper.getLocalPage() != null) { if (PageHelper.getLocalPage() != null) {
return; return;
@ -69,6 +89,20 @@ public class PaginationService {
setPagination(1, this.maxPageSize, sortBy, sortOrder, table); 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<Object> setPagination( public com.github.pagehelper.Page<Object> setPagination(
final Integer pageNumber, final Integer pageNumber,
final Integer pageSize, final Integer pageSize,
@ -76,18 +110,8 @@ public class PaginationService {
final Page.SortOrder sortOrder, final Page.SortOrder sortOrder,
final SqlTable table) { 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<Object> startPage = final com.github.pagehelper.Page<Object> startPage =
PageHelper.startPage(_pageNumber, _pageSize, true, true, false); PageHelper.startPage(getPageNumber(pageNumber), getPageSize(pageSize), true, true, false);
final String sortColumnName = verifySortColumnName(sortBy, table); final String sortColumnName = verifySortColumnName(sortBy, table);
if (StringUtils.isNoneBlank(sortColumnName)) { if (StringUtils.isNoneBlank(sortColumnName)) {
@ -111,7 +135,7 @@ public class PaginationService {
final com.github.pagehelper.Page<Object> page = setPagination(pageNumber, pageSize, sortBy, sortOrder, table); final com.github.pagehelper.Page<Object> page = setPagination(pageNumber, pageSize, sortBy, sortOrder, table);
final Collection<T> pageList = delegate.get(); final Collection<T> 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) { 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? // TODO is it possible to generate this within MyBatis generator?
private void initSortColumnMapping() { private void initSortColumnMapping() {
// User Table // User Table
final Map<String, String> userTableMap = new HashMap<>(); final Map<String, String> userTableMap = new HashMap<>();
userTableMap.put(Domain.USER.ATTR_NAME, UserRecordDynamicSqlSupport.name.name()); userTableMap.put(Domain.USER.ATTR_NAME, UserRecordDynamicSqlSupport.name.name());
@ -163,6 +188,21 @@ public class PaginationService {
UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord.name(), UserActivityLogRecordDynamicSqlSupport.userActivityLogRecord.name(),
Domain.USER_ACTIVITY_LOG.ATTR_ID); Domain.USER_ACTIVITY_LOG.ATTR_ID);
// Exam Table
final Map<String, String> 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());
} }
} }

View file

@ -22,7 +22,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
public interface AuthorizationGrantService { public interface AuthorizationGrantService {
/** Gets the UserService that is bundled within the AuthorizationGrantService /** Gets the UserService that is bundled within the AuthorizationGrantService
* *
* @return the UserService that is bundled within the AuthorizationGrantService */ * @return the UserService that is bundled within the AuthorizationGrantService */
UserService getUserService(); UserService getUserService();
@ -33,6 +33,10 @@ public interface AuthorizationGrantService {
* @param privilegeType the PrivilegeType to check on EntityType */ * @param privilegeType the PrivilegeType to check on EntityType */
void checkHasAnyPrivilege(EntityType entityType, PrivilegeType privilegeType); 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 /** Check if current user has grant on a given GrantEntity instance for specified PrivilegeType
* *
* @param entity The GrantEntity to check specified PrivilegeType for * @param entity The GrantEntity to check specified PrivilegeType for

View file

@ -120,49 +120,86 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
} }
@Override @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(); final SEBServerUser currentUser = this.userService.getCurrentUser();
if (hasBasePrivilege(entityType, grantType, currentUser) || if (hasBasePrivilege(entityType, privilegeType, currentUser) ||
hasInstitutionalPrivilege(entityType, grantType, currentUser)) { hasInstitutionalPrivilege(entityType, privilegeType, currentUser)) {
return; return;
} }
throw new PermissionDeniedException(entityType, grantType, currentUser.getUserInfo().uuid); throw new PermissionDeniedException(entityType, privilegeType, currentUser.getUserInfo().uuid);
} }
@Override @Override
public <E extends GrantEntity> Result<E> 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(); 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 <E extends GrantEntity> Result<E> checkGrantOnEntity(final E entity, final PrivilegeType privilegeType) {
final SEBServerUser currentUser = this.userService.getCurrentUser();
if (hasGrant(entity, privilegeType, currentUser)) {
return Result.of(entity); return Result.of(entity);
} else { } else {
return Result.ofError(new PermissionDeniedException(entity, grantType, currentUser.getUserInfo().uuid)); return Result.ofError(new PermissionDeniedException(entity, privilegeType, currentUser.getUserInfo().uuid));
} }
} }
@Override @Override
public boolean hasBasePrivilege(final EntityType entityType, final PrivilegeType grantType) { public boolean hasBasePrivilege(final EntityType entityType, final PrivilegeType privilegeType) {
return hasBasePrivilege(entityType, grantType, this.userService.getCurrentUser()); return hasBasePrivilege(entityType, privilegeType, this.userService.getCurrentUser());
} }
@Override @Override
public boolean hasBasePrivilege( public boolean hasBasePrivilege(
final EntityType entityType, final EntityType entityType,
final PrivilegeType grantType, final PrivilegeType privilegeType,
final Principal principal) { final Principal principal) {
return hasBasePrivilege(entityType, grantType, this.userService.extractFromPrincipal(principal)); return hasBasePrivilege(entityType, privilegeType, this.userService.extractFromPrincipal(principal));
} }
private boolean hasBasePrivilege( private boolean hasBasePrivilege(
final EntityType entityType, final EntityType entityType,
final PrivilegeType grantType, final PrivilegeType privilegeType,
final SEBServerUser user) { final SEBServerUser user) {
for (final UserRole role : user.getUserRoles()) { for (final UserRole role : user.getUserRoles()) {
final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role)); final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role));
if (roleTypeGrant != null && roleTypeGrant.hasBasePrivilege(grantType)) { if (roleTypeGrant != null && roleTypeGrant.hasBasePrivilege(privilegeType)) {
return true; return true;
} }
} }
@ -171,27 +208,36 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
} }
@Override @Override
public boolean hasInstitutionalPrivilege(final EntityType entityType, final PrivilegeType grantType) { public boolean hasInstitutionalPrivilege(
return hasInstitutionalPrivilege(entityType, grantType, this.userService.getCurrentUser()); final EntityType entityType,
final PrivilegeType privilegeType) {
return hasInstitutionalPrivilege(
entityType,
privilegeType,
this.userService.getCurrentUser());
} }
@Override @Override
public boolean hasInstitutionalPrivilege( public boolean hasInstitutionalPrivilege(
final EntityType entityType, final EntityType entityType,
final PrivilegeType grantType, final PrivilegeType privilegeType,
final Principal principal) { final Principal principal) {
return hasInstitutionalPrivilege(entityType, grantType, this.userService.extractFromPrincipal(principal)); return hasInstitutionalPrivilege(
entityType,
privilegeType,
this.userService.extractFromPrincipal(principal));
} }
private boolean hasInstitutionalPrivilege( private boolean hasInstitutionalPrivilege(
final EntityType entityType, final EntityType entityType,
final PrivilegeType grantType, final PrivilegeType privilegeType,
final SEBServerUser user) { final SEBServerUser user) {
for (final UserRole role : user.getUserRoles()) { for (final UserRole role : user.getUserRoles()) {
final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role)); final Privilege roleTypeGrant = this.grants.get(new RoleTypeKey(entityType, role));
if (roleTypeGrant != null && roleTypeGrant.hasInstitutionalPrivilege(grantType)) { if (roleTypeGrant != null && roleTypeGrant.hasInstitutionalPrivilege(privilegeType)) {
return true; return true;
} }
} }

View file

@ -10,9 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
import java.security.Principal; import java.security.Principal;
import org.springframework.web.bind.WebDataBinder;
/** A service to get the authenticated user from current request */ /** A service to get the authenticated user from current request */
public interface UserService { 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. /** Use this to get the current User within a request-response thread cycle.
* *
* @return the SEBServerUser instance of the current request * @return the SEBServerUser instance of the current request
@ -46,4 +50,6 @@ public interface UserService {
* @return an overall super user with all rights */ * @return an overall super user with all rights */
SEBServerUser getSuperUser(); SEBServerUser getSuperUser();
void addUsersInstitutionDefaultPropertySupport(final WebDataBinder binder);
} }

View file

@ -8,6 +8,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.authorization; package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
import java.beans.PropertyEditorSupport;
import java.security.Principal; import java.security.Principal;
import java.util.Collection; 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.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; 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.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
@ -78,6 +80,22 @@ public class UserServiceImpl implements UserService {
return SUPER_USER; 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 // 1. OAuth2Authentication strategy
@Lazy @Lazy
@Component @Component

View file

@ -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.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; 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; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
@Service @Service
@ -49,44 +50,56 @@ public class BulkActionService {
action.alreadyProcessed = true; action.alreadyProcessed = true;
} }
public void doBulkAction(final BulkAction action) { public Result<BulkAction> doBulkAction(final BulkAction action) {
checkProcessing(action); return Result.tryCatch(() -> {
final BulkActionSupportDAO<?> supportForSource = this.supporter.get(action.sourceType); checkProcessing(action);
if (supportForSource == null) {
action.alreadyProcessed = true;
return;
}
collectDependencies(action); final BulkActionSupportDAO<?> supportForSource = this.supporter
.get(action.sourceType);
if (!action.dependencies.isEmpty()) { if (supportForSource == null) {
// process dependencies first... action.alreadyProcessed = true;
final List<BulkActionSupportDAO<?>> dependancySupporter = throw new IllegalArgumentException("No bulk action support for: " + action);
getDependancySupporter(action);
for (final BulkActionSupportDAO<?> support : dependancySupporter) {
action.result.addAll(support.processBulkAction(action));
} }
}
action.result.addAll(supportForSource.processBulkAction(action)); collectDependencies(action);
processUserActivityLog(action); if (!action.dependencies.isEmpty()) {
action.alreadyProcessed = true; // process dependencies first...
final List<BulkActionSupportDAO<?>> 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<EntityProcessingReport> createReport(final BulkAction action) {
if (!action.alreadyProcessed) { if (!action.alreadyProcessed) {
doBulkAction(action); return doBulkAction(action)
.flatMap(this::createFullReport);
} else {
return createFullReport(action);
} }
}
// TODO private Result<EntityProcessingReport> createFullReport(final BulkAction action) {
return Result.tryCatch(() -> {
return new EntityProcessingReport( // TODO
Collections.emptyList(), return new EntityProcessingReport(
Collections.emptyList(), action.sources,
Collections.emptyMap()); Collections.emptyList(),
Collections.emptyList());
});
} }
private void processUserActivityLog(final BulkAction action) { private void processUserActivityLog(final BulkAction action) {
@ -98,7 +111,7 @@ public class BulkActionService {
this.userActivityLogDAO.log( this.userActivityLogDAO.log(
action.type.activityType, action.type.activityType,
key.entityType, key.entityType,
key.entityId, key.modelId,
"bulk action dependency"); "bulk action dependency");
} }
@ -106,7 +119,7 @@ public class BulkActionService {
this.userActivityLogDAO.log( this.userActivityLogDAO.log(
action.type.activityType, action.type.activityType,
key.entityType, key.entityType,
key.entityId, key.modelId,
"bulk action source"); "bulk action source");
} }
} }

View file

@ -32,7 +32,21 @@ public interface EntityDAO<T extends Entity> {
* @param id the data base identifier of the entity * @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 * @return Result refer the Entity instance with the specified database identifier or refer to an error if
* happened */ * happened */
Result<T> byId(Long id); Result<T> 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<T> 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. /** Use this to get a Collection of all entities of concrete type that matches a given predicate.
* *
@ -104,7 +118,7 @@ public interface EntityDAO<T extends Entity> {
* *
* @param keys Collection of EntityKey of various types * @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 */ * @return List of id's (PK's) from the given key collection that match the concrete EntityType */
default List<Long> extractIdsFromKeys(final Collection<EntityKey> keys) { default List<Long> extractPKsFromKeys(final Collection<EntityKey> keys) {
if (keys == null) { if (keys == null) {
return Collections.emptyList(); return Collections.emptyList();
@ -114,7 +128,7 @@ public interface EntityDAO<T extends Entity> {
return keys return keys
.stream() .stream()
.filter(key -> key.entityType == entityType) .filter(key -> key.entityType == entityType)
.map(key -> Long.valueOf(key.entityId)) .map(key -> Long.valueOf(key.modelId))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View file

@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
import java.util.Collection; 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.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -27,8 +29,10 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam>, BulkActionSupportDA
String name, String name,
Exam.ExamStatus status, Exam.ExamStatus status,
Exam.ExamType type, Exam.ExamType type,
Long startTime, DateTime from,
String owner, String owner,
Boolean active); Boolean active);
Result<Exam> save(Exam exam);
} }

View file

@ -84,6 +84,7 @@ public interface UserActivityLogDAO extends UserRelatedEntityDAO<UserActivityLog
} }
Result<Collection<UserActivityLog>> all( Result<Collection<UserActivityLog>> all(
Long InstitutionId,
String userId, String userId,
Long from, Long from,
Long to, Long to,

View file

@ -26,17 +26,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSuppor
* within SEBServerUser. */ * within SEBServerUser. */
public interface UserDAO extends ActivatableEntityDAO<UserInfo>, BulkActionSupportDAO<UserInfo> { public interface UserDAO extends ActivatableEntityDAO<UserInfo>, BulkActionSupportDAO<UserInfo> {
/** 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<UserInfo> byUuid(String uuid);
/** Use this to get the user id (PK) from a given UUID. /** Use this to get the user id (PK) from a given UUID.
* *
* @param uuid The UUID of the user * @param uuid The UUID of the user
* @return the user id (PK) from a given UUID. */ * @return the user id (PK) from a given UUID. */
Result<Long> pkForUUID(String uuid); Result<Long> pkForModelId(String uuid);
/** Use this to get UserInfo by users username /** Use this to get UserInfo by users username
* *

View file

@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
import org.mybatis.dynamic.sql.select.QueryExpressionDSL; import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -67,7 +68,7 @@ public class ExamDAOImpl implements ExamDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Exam> byId(final Long id) { public Result<Exam> byPK(final Long id) {
return recordById(id) return recordById(id)
.flatMap(this::toDomainModel); .flatMap(this::toDomainModel);
} }
@ -108,7 +109,7 @@ public class ExamDAOImpl implements ExamDAO {
final String name, final String name,
final ExamStatus status, final ExamStatus status,
final ExamType type, final ExamType type,
final Long startTime, final DateTime from,
final String owner, final String owner,
final Boolean active) { final Boolean active) {
@ -121,8 +122,8 @@ public class ExamDAOImpl implements ExamDAO {
} }
} }
if (startTime != null) { if (from != null) {
if (exam.startTime.getMillis() < startTime.longValue()) { if (exam.startTime.isAfter(from)) {
return false; return false;
} }
} }
@ -167,10 +168,40 @@ public class ExamDAOImpl implements ExamDAO {
return Result.ofTODO(); return Result.ofTODO();
} }
@Override
public Result<Exam> 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<ExamRecord> 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 @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) { public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
final ExamRecord examRecord = new ExamRecord(null, null, null, null, null, final ExamRecord examRecord = new ExamRecord(null, null, null, null, null,
null, null, null, BooleanUtils.toInteger(active)); null, null, null, BooleanUtils.toInteger(active));
@ -196,7 +227,7 @@ public class ExamDAOImpl implements ExamDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) { public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
try { try {
@ -235,7 +266,7 @@ public class ExamDAOImpl implements ExamDAO {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<Exam>> bulkLoadEntities(final Collection<EntityKey> keys) { public Result<Collection<Exam>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final List<Long> ids = extractIdsFromKeys(keys); final List<Long> ids = extractPKsFromKeys(keys);
return this.examRecordMapper.selectByExample() return this.examRecordMapper.selectByExample()
.where(ExamRecordDynamicSqlSupport.id, isIn(ids)) .where(ExamRecordDynamicSqlSupport.id, isIn(ids))
.build() .build()
@ -247,7 +278,7 @@ public class ExamDAOImpl implements ExamDAO {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
return this.examRecordMapper.selectIdsByExample() return this.examRecordMapper.selectIdsByExample()
.where(ExamRecordDynamicSqlSupport.institutionId, .where(ExamRecordDynamicSqlSupport.institutionId,
isEqualTo(Long.valueOf(institutionKey.entityId))) isEqualTo(Long.valueOf(institutionKey.modelId)))
.build() .build()
.execute() .execute()
.stream() .stream()
@ -260,7 +291,7 @@ public class ExamDAOImpl implements ExamDAO {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
return this.examRecordMapper.selectIdsByExample() return this.examRecordMapper.selectIdsByExample()
.where(ExamRecordDynamicSqlSupport.lmsSetupId, .where(ExamRecordDynamicSqlSupport.lmsSetupId,
isEqualTo(Long.valueOf(lmsSetupKey.entityId))) isEqualTo(Long.valueOf(lmsSetupKey.modelId)))
.build() .build()
.execute() .execute()
.stream() .stream()

View file

@ -56,7 +56,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Institution> byId(final Long id) { public Result<Institution> byPK(final Long id) {
return recordById(id) return recordById(id)
.flatMap(InstitutionDAOImpl::toDomainModel); .flatMap(InstitutionDAOImpl::toDomainModel);
} }
@ -123,7 +123,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) { public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
final InstitutionRecord institutionRecord = new InstitutionRecord( final InstitutionRecord institutionRecord = new InstitutionRecord(
null, null, null, BooleanUtils.toInteger(active), null); null, null, null, BooleanUtils.toInteger(active), null);
@ -147,7 +147,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) { public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
try { try {
this.institutionRecordMapper.deleteByExample() this.institutionRecordMapper.deleteByExample()
@ -177,7 +177,7 @@ public class InstitutionDAOImpl implements InstitutionDAO {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<Institution>> bulkLoadEntities(final Collection<EntityKey> keys) { public Result<Collection<Institution>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final List<Long> ids = extractIdsFromKeys(keys); final List<Long> ids = extractPKsFromKeys(keys);
return this.institutionRecordMapper.selectByExample() return this.institutionRecordMapper.selectByExample()
.where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
@ -217,19 +217,18 @@ public class InstitutionDAOImpl implements InstitutionDAO {
} }
private Result<InstitutionRecord> update(final Institution institution) { private Result<InstitutionRecord> update(final Institution institution) {
return recordById(institution.id) return Result.tryCatch(() -> {
.map(record -> {
final InstitutionRecord newRecord = new InstitutionRecord( final InstitutionRecord newRecord = new InstitutionRecord(
institution.id, institution.id,
institution.name, institution.name,
institution.urlSuffix, institution.urlSuffix,
null, null,
institution.logoImage); institution.logoImage);
this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord); this.institutionRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.institutionRecordMapper.selectByPrimaryKey(institution.id); return this.institutionRecordMapper.selectByPrimaryKey(institution.id);
}); });
} }
private static Result<Institution> toDomainModel(final InstitutionRecord record) { private static Result<Institution> toDomainModel(final InstitutionRecord record) {

View file

@ -55,7 +55,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<LmsSetup> byId(final Long id) { public Result<LmsSetup> byPK(final Long id) {
return recordById(id) return recordById(id)
.flatMap(LmsSetupDAOImpl::toDomainModel); .flatMap(LmsSetupDAOImpl::toDomainModel);
} }
@ -137,7 +137,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) { public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
final LmsSetupRecord lmsSetupRecord = new LmsSetupRecord( final LmsSetupRecord lmsSetupRecord = new LmsSetupRecord(
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
BooleanUtils.toIntegerObject(active)); BooleanUtils.toIntegerObject(active));
@ -162,7 +162,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) { public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
try { try {
this.lmsSetupRecordMapper.deleteByExample() this.lmsSetupRecordMapper.deleteByExample()
@ -196,7 +196,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<LmsSetup>> bulkLoadEntities(final Collection<EntityKey> keys) { public Result<Collection<LmsSetup>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final List<Long> ids = extractIdsFromKeys(keys); final List<Long> ids = extractPKsFromKeys(keys);
return this.lmsSetupRecordMapper.selectByExample() return this.lmsSetupRecordMapper.selectByExample()
.where(LmsSetupRecordDynamicSqlSupport.id, isIn(ids)) .where(LmsSetupRecordDynamicSqlSupport.id, isIn(ids))
@ -213,7 +213,7 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
return this.lmsSetupRecordMapper.selectIdsByExample() return this.lmsSetupRecordMapper.selectIdsByExample()
.where(LmsSetupRecordDynamicSqlSupport.institutionId, .where(LmsSetupRecordDynamicSqlSupport.institutionId,
isEqualTo(Long.valueOf(institutionKey.entityId))) isEqualTo(Long.valueOf(institutionKey.modelId)))
.build() .build()
.execute() .execute()
.stream() .stream()
@ -271,25 +271,24 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
} }
private Result<LmsSetupRecord> update(final LmsSetup lmsSetup) { private Result<LmsSetupRecord> update(final LmsSetup lmsSetup) {
return recordById(lmsSetup.id) return Result.tryCatch(() -> {
.map(record -> {
final LmsSetupRecord newRecord = new LmsSetupRecord( final LmsSetupRecord newRecord = new LmsSetupRecord(
lmsSetup.id, lmsSetup.id,
lmsSetup.institutionId, lmsSetup.institutionId,
lmsSetup.name, lmsSetup.name,
(lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null, (lmsSetup.lmsType != null) ? lmsSetup.lmsType.name() : null,
lmsSetup.lmsApiUrl, lmsSetup.lmsApiUrl,
lmsSetup.lmsAuthName, lmsSetup.lmsAuthName,
lmsSetup.lmsAuthSecret, lmsSetup.lmsAuthSecret,
lmsSetup.lmsRestApiToken, lmsSetup.lmsRestApiToken,
lmsSetup.sebAuthName, lmsSetup.sebAuthName,
lmsSetup.sebAuthSecret, lmsSetup.sebAuthSecret,
null); null);
this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord); this.lmsSetupRecordMapper.updateByPrimaryKeySelective(newRecord);
return this.lmsSetupRecordMapper.selectByPrimaryKey(lmsSetup.id); return this.lmsSetupRecordMapper.selectByPrimaryKey(lmsSetup.id);
}); });
} }
} }

View file

@ -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.mapper.UserRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.UserActivityLogRecord; 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.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.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
@ -49,18 +47,15 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
private final UserActivityLogRecordMapper userLogRecordMapper; private final UserActivityLogRecordMapper userLogRecordMapper;
private final UserService userService; private final UserService userService;
private final AuthorizationGrantService authorizationGrantService;
private final PaginationService paginationService; private final PaginationService paginationService;
public UserActivityLogDAOImpl( public UserActivityLogDAOImpl(
final UserActivityLogRecordMapper userLogRecordMapper, final UserActivityLogRecordMapper userLogRecordMapper,
final UserService userService, final UserService userService,
final AuthorizationGrantService authorizationGrantService,
final PaginationService paginationService) { final PaginationService paginationService) {
this.userLogRecordMapper = userLogRecordMapper; this.userLogRecordMapper = userLogRecordMapper;
this.userService = userService; this.userService = userService;
this.authorizationGrantService = authorizationGrantService;
this.paginationService = paginationService; this.paginationService = paginationService;
} }
@ -172,7 +167,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<UserActivityLog> byId(final Long id) { public Result<UserActivityLog> byPK(final Long id) {
return Result.tryCatch(() -> this.userLogRecordMapper.selectByPrimaryKey(id)) return Result.tryCatch(() -> this.userLogRecordMapper.selectByPrimaryKey(id))
.flatMap(UserActivityLogDAOImpl::toDomainModel); .flatMap(UserActivityLogDAOImpl::toDomainModel);
} }
@ -180,7 +175,7 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) { public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
try { try {
this.userLogRecordMapper.deleteByExample() this.userLogRecordMapper.deleteByExample()
@ -202,12 +197,24 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<UserActivityLog>> getAllForUser(final String userUuid) { public Result<Collection<UserActivityLog>> 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 @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<UserActivityLog>> all( public Result<Collection<UserActivityLog>> all(
final Long institutionId,
final String userId, final String userId,
final Long from, final Long from,
final Long to, final Long to,
@ -218,57 +225,30 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
? predicate ? predicate
: model -> true; : model -> true;
final boolean basePrivilege = this.authorizationGrantService.hasBasePrivilege( return this.userLogRecordMapper.selectByExample()
EntityType.USER_ACTIVITY_LOG, .join(UserRecordDynamicSqlSupport.userRecord)
PrivilegeType.READ_ONLY); .on(
UserRecordDynamicSqlSupport.uuid,
final Long institutionId = (basePrivilege) SqlBuilder.equalTo(UserActivityLogRecordDynamicSqlSupport.userUuid))
? null .where(
: this.userService.getCurrentUser().institutionId(); UserRecordDynamicSqlSupport.institutionId,
SqlBuilder.isEqualTo(institutionId))
return (institutionId == null) .and(
? this.userLogRecordMapper.selectByExample() UserActivityLogRecordDynamicSqlSupport.userUuid,
.where( SqlBuilder.isEqualToWhenPresent(userId))
UserActivityLogRecordDynamicSqlSupport.userUuid, .and(
SqlBuilder.isEqualToWhenPresent(userId)) UserActivityLogRecordDynamicSqlSupport.timestamp,
.and( SqlBuilder.isGreaterThanOrEqualToWhenPresent(from))
UserActivityLogRecordDynamicSqlSupport.timestamp, .and(
SqlBuilder.isGreaterThanOrEqualToWhenPresent(from)) UserActivityLogRecordDynamicSqlSupport.timestamp,
.and( SqlBuilder.isLessThanWhenPresent(to))
UserActivityLogRecordDynamicSqlSupport.timestamp, .build()
SqlBuilder.isLessThanWhenPresent(to)) .execute()
.build() .stream()
.execute() .filter(_predicate)
.stream() .map(UserActivityLogDAOImpl::toDomainModel)
.filter(_predicate) .flatMap(Result::skipOnError)
.map(UserActivityLogDAOImpl::toDomainModel) .collect(Collectors.toList());
.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());
}); });
} }

View file

@ -83,22 +83,22 @@ public class UserDaoImpl implements UserDAO {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<UserInfo> byId(final Long id) { public Result<UserInfo> byPK(final Long id) {
return Result.tryCatch(() -> this.userRecordMapper.selectByPrimaryKey(id)) return Result.tryCatch(() -> this.userRecordMapper.selectByPrimaryKey(id))
.flatMap(this::toDomainModel); .flatMap(this::toDomainModel);
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<UserInfo> byUuid(final String uuid) { public Result<UserInfo> byModelId(final String modelId) {
return recordByUUID(uuid) return recordByUUID(modelId)
.flatMap(this::toDomainModel); .flatMap(this::toDomainModel);
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Long> pkForUUID(final String uuid) { public Result<Long> pkForModelId(final String modelId) {
return recordByUUID(uuid) return recordByUUID(modelId)
.map(r -> r.getId()); .map(r -> r.getId());
} }
@ -191,7 +191,7 @@ public class UserDaoImpl implements UserDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) { public Collection<Result<EntityKey>> setActive(final Set<EntityKey> all, final boolean active) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
final UserRecord userRecord = new UserRecord( final UserRecord userRecord = new UserRecord(
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
BooleanUtils.toIntegerObject(active)); BooleanUtils.toIntegerObject(active));
@ -216,7 +216,7 @@ public class UserDaoImpl implements UserDAO {
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) { public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
final List<Long> ids = extractIdsFromKeys(all); final List<Long> ids = extractPKsFromKeys(all);
try { try {
this.userRecordMapper.deleteByExample() this.userRecordMapper.deleteByExample()
@ -259,7 +259,7 @@ public class UserDaoImpl implements UserDAO {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Result<Collection<UserInfo>> bulkLoadEntities(final Collection<EntityKey> keys) { public Result<Collection<UserInfo>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final List<Long> ids = extractIdsFromKeys(keys); final List<Long> ids = extractPKsFromKeys(keys);
return this.userRecordMapper.selectByExample() return this.userRecordMapper.selectByExample()
.where(InstitutionRecordDynamicSqlSupport.id, isIn(ids)) .where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
@ -273,12 +273,12 @@ public class UserDaoImpl implements UserDAO {
} }
@Override @Override
public List<Long> extractIdsFromKeys(final Collection<EntityKey> keys) { public List<Long> extractPKsFromKeys(final Collection<EntityKey> keys) {
if (keys == null || keys.isEmpty() || keys.iterator().next().isIdPK) { if (keys == null || keys.isEmpty() || keys.iterator().next().isIdPK) {
return UserDAO.super.extractIdsFromKeys(keys); return UserDAO.super.extractPKsFromKeys(keys);
} else { } else {
final List<String> uuids = keys.stream() final List<String> uuids = keys.stream()
.map(key -> key.entityId) .map(key -> key.modelId)
.collect(Collectors.toList()); .collect(Collectors.toList());
try { try {
@ -299,7 +299,7 @@ public class UserDaoImpl implements UserDAO {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
return this.userRecordMapper.selectIdsByExample() return this.userRecordMapper.selectIdsByExample()
.where(UserRecordDynamicSqlSupport.institutionId, .where(UserRecordDynamicSqlSupport.institutionId,
isEqualTo(Long.valueOf(institutionKey.entityId))) isEqualTo(Long.valueOf(institutionKey.modelId)))
.build() .build()
.execute() .execute()
.stream() .stream()

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms; 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.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -17,8 +19,8 @@ public interface LmsAPIService {
Result<LmsAPITemplate> createLmsAPITemplate(LmsSetup lmsSetup); Result<LmsAPITemplate> createLmsAPITemplate(LmsSetup lmsSetup);
Result<byte[]> createSEBStartConfiguration(Long lmsSetupId); Result<InputStream> createSEBStartConfiguration(Long lmsSetupId);
Result<byte[]> createSEBStartConfiguration(LmsSetup lmsSetup); Result<InputStream> createSEBStartConfiguration(LmsSetup lmsSetup);
} }

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
import java.io.InputStream;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
@ -30,7 +32,7 @@ public class LmsAPIServiceImpl implements LmsAPIService {
@Override @Override
public Result<LmsAPITemplate> createLmsAPITemplate(final Long lmsSetupId) { public Result<LmsAPITemplate> createLmsAPITemplate(final Long lmsSetupId) {
return this.lmsSetupDAO return this.lmsSetupDAO
.byId(lmsSetupId) .byPK(lmsSetupId)
.flatMap(this::createLmsAPITemplate); .flatMap(this::createLmsAPITemplate);
} }
@ -46,14 +48,14 @@ public class LmsAPIServiceImpl implements LmsAPIService {
} }
@Override @Override
public Result<byte[]> createSEBStartConfiguration(final Long lmsSetupId) { public Result<InputStream> createSEBStartConfiguration(final Long lmsSetupId) {
return this.lmsSetupDAO return this.lmsSetupDAO
.byId(lmsSetupId) .byPK(lmsSetupId)
.flatMap(this::createSEBStartConfiguration); .flatMap(this::createSEBStartConfiguration);
} }
@Override @Override
public Result<byte[]> createSEBStartConfiguration(final LmsSetup lmsSetup) { public Result<InputStream> createSEBStartConfiguration(final LmsSetup lmsSetup) {
// TODO implementation of creation of SEB start configuration for specified 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 // A SEB start configuration should at least contain the SEB-Client-Credentials to access the SEB Server API

View file

@ -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<T extends GrantEntity> extends EntityController<T> {
public ActivatableEntityController(
final AuthorizationGrantService authorizationGrantService,
final BulkActionService bulkActionService,
final EntityDAO<T> 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<EntityProcessingReport> 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));
}
}

View file

@ -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<T extends GrantEntity> {
protected final AuthorizationGrantService authorizationGrantService;
protected final BulkActionService bulkActionService;
protected final EntityDAO<T> entityDAO;
protected EntityController(
final AuthorizationGrantService authorizationGrantService,
final BulkActionService bulkActionService,
final EntityDAO<T> 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();
}
}

View file

@ -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<Exam> 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<Exam> 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<Exam> 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<EntityProcessingReport> 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<Exam> 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);
}
}

View file

@ -21,8 +21,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; 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.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport; import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
@ -62,24 +62,18 @@ public class InstitutionController {
@RequestMapping(path = "/self", method = RequestMethod.GET) @RequestMapping(path = "/self", method = RequestMethod.GET)
public Institution getOwn() { public Institution getOwn() {
checkBaseReadPrivilege();
final SEBServerUser currentUser = this.authorizationGrantService final SEBServerUser currentUser = this.authorizationGrantService
.getUserService() .getUserService()
.getCurrentUser(); .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) @RequestMapping(path = "/{id}", method = RequestMethod.GET)
public Institution getById(@PathVariable final Long id) { public Institution getById(@PathVariable final Long id) {
checkBaseReadPrivilege();
return this.institutionDAO return this.institutionDAO
.byId(id) .byPK(id)
.flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity( .flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity(
inst, inst,
PrivilegeType.READ_ONLY)) PrivilegeType.READ_ONLY))
@ -90,8 +84,6 @@ public class InstitutionController {
public Collection<Institution> getAll( public Collection<Institution> getAll(
@RequestParam(name = Institution.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { @RequestParam(name = Institution.FILTER_ATTR_ACTIVE, required = false) final Boolean active) {
checkBaseReadPrivilege();
if (!this.authorizationGrantService.hasBasePrivilege( if (!this.authorizationGrantService.hasBasePrivilege(
EntityType.INSTITUTION, EntityType.INSTITUTION,
PrivilegeType.READ_ONLY)) { PrivilegeType.READ_ONLY)) {
@ -126,13 +118,15 @@ public class InstitutionController {
} }
@RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) @RequestMapping(path = "/{id}/activate", method = RequestMethod.POST)
public Institution activate(@PathVariable final Long id) { public EntityProcessingReport activate(@PathVariable final Long id) {
return setActive(id, true); return setActive(id, true)
.getOrThrow();
} }
@RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) @RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST)
public Institution deactivate(@PathVariable final Long id) { public EntityProcessingReport deactivate(@PathVariable final Long id) {
return setActive(id, false); return setActive(id, false)
.getOrThrow();
} }
@RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE) @RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE)
@ -142,7 +136,8 @@ public class InstitutionController {
return this.bulkActionService.createReport(new BulkAction( return this.bulkActionService.createReport(new BulkAction(
Type.DEACTIVATE, Type.DEACTIVATE,
EntityType.INSTITUTION, EntityType.INSTITUTION,
new EntityKey(id, EntityType.INSTITUTION))); new EntityKey(id, EntityType.INSTITUTION)))
.getOrThrow();
} }
@RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE) @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE)
@ -152,7 +147,8 @@ public class InstitutionController {
return this.bulkActionService.createReport(new BulkAction( return this.bulkActionService.createReport(new BulkAction(
Type.HARD_DELETE, Type.HARD_DELETE,
EntityType.INSTITUTION, EntityType.INSTITUTION,
new EntityKey(id, EntityType.INSTITUTION))); new EntityKey(id, EntityType.INSTITUTION)))
.getOrThrow();
} }
private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) { private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) {
@ -160,24 +156,21 @@ public class InstitutionController {
EntityType.INSTITUTION, EntityType.INSTITUTION,
type); type);
this.institutionDAO.byId(id) this.institutionDAO.byPK(id)
.flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity( .flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity(
institution, institution,
type)) type))
.getOrThrow(); .getOrThrow();
} }
private Institution setActive(final Long id, final boolean active) { private Result<EntityProcessingReport> setActive(final Long id, final boolean active) {
checkPrivilegeForInstitution(id, PrivilegeType.MODIFY); checkPrivilegeForInstitution(id, PrivilegeType.MODIFY);
this.bulkActionService.doBulkAction(new BulkAction( return this.bulkActionService.createReport(new BulkAction(
(active) ? Type.ACTIVATE : Type.DEACTIVATE, (active) ? Type.ACTIVATE : Type.DEACTIVATE,
EntityType.INSTITUTION, EntityType.INSTITUTION,
new EntityKey(id, EntityType.INSTITUTION))); new EntityKey(id, EntityType.INSTITUTION)));
return this.institutionDAO
.byId(id)
.getOrThrow();
} }
private Result<Institution> save(final Institution institution, final PrivilegeType privilegeType) { private Result<Institution> save(final Institution institution, final PrivilegeType privilegeType) {
@ -192,10 +185,4 @@ public class InstitutionController {
.flatMap(inst -> this.userActivityLogDAO.log(activityType, inst)); .flatMap(inst -> this.userActivityLogDAO.log(activityType, inst));
} }
private void checkBaseReadPrivilege() {
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.INSTITUTION,
PrivilegeType.READ_ONLY);
}
} }

View file

@ -8,12 +8,18 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.io.InputStream;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; 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.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.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; 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.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; 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.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.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;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction.Type; 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.bulkaction.BulkActionService;
@ -66,44 +71,42 @@ public class LmsSetupController {
this.lmsAPIService = lmsAPIService; this.lmsAPIService = lmsAPIService;
} }
@InitBinder
public void initBinder(final WebDataBinder binder) throws Exception {
this.authorizationGrantService
.getUserService()
.addUsersInstitutionDefaultPropertySupport(binder);
}
@RequestMapping(method = RequestMethod.GET) @RequestMapping(method = RequestMethod.GET)
public Collection<LmsSetup> getAll( public Collection<LmsSetup> 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_NAME, required = false) final String name,
@RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LmsType lmsType, @RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LmsType lmsType,
@RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { @RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) {
checkBaseReadPrivilege(); checkReadPrivilege(institutionId);
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);
}
return this.lmsSetupDAO return this.lmsSetupDAO
.allMatching(instId, name, lmsType, active) .allMatching(institutionId, name, lmsType, active)
.getOrThrow(); .getOrThrow();
} }
@RequestMapping(path = "/names", method = RequestMethod.GET) @RequestMapping(path = "/names", method = RequestMethod.GET)
public Collection<EntityKeyAndName> getNames( public Collection<EntityKeyAndName> 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_NAME, required = false) final String name,
@RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LmsType lmsType, @RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LmsType lmsType,
@RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { @RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) {
checkReadPrivilege(institutionId);
return getAll(institutionId, name, lmsType, active) return getAll(institutionId, name, lmsType, active)
.stream() .stream()
.map(LmsSetup::toName) .map(LmsSetup::toName)
@ -112,13 +115,10 @@ public class LmsSetupController {
@RequestMapping(path = "/{id}", method = RequestMethod.GET) @RequestMapping(path = "/{id}", method = RequestMethod.GET)
public LmsSetup getById(@PathVariable final Long id) { public LmsSetup getById(@PathVariable final Long id) {
checkBaseReadPrivilege();
return this.lmsSetupDAO return this.lmsSetupDAO
.byId(id) .byPK(id)
.flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity( .flatMap(lmsSetup -> this.authorizationGrantService.checkGrantOnEntity(
inst, lmsSetup,
PrivilegeType.READ_ONLY)) PrivilegeType.READ_ONLY))
.getOrThrow(); .getOrThrow();
} }
@ -127,15 +127,28 @@ public class LmsSetupController {
path = "/create_seb_config/{id}", path = "/create_seb_config/{id}",
method = RequestMethod.GET, method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) // TODO check if this is the right format 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( this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.LMS_SETUP, EntityType.LMS_SETUP,
PrivilegeType.WRITE); PrivilegeType.WRITE);
return this.lmsAPIService response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
.createSEBStartConfiguration(id) response.setStatus(HttpStatus.OK.value());
.getOrThrow();
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) @RequestMapping(path = "/create", method = RequestMethod.PUT)
@ -151,12 +164,12 @@ public class LmsSetupController {
} }
@RequestMapping(path = "/{id}/activate", method = RequestMethod.POST) @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); return setActive(id, true);
} }
@RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST) @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); return setActive(id, false);
} }
@ -167,7 +180,8 @@ public class LmsSetupController {
return this.bulkActionService.createReport(new BulkAction( return this.bulkActionService.createReport(new BulkAction(
Type.DEACTIVATE, Type.DEACTIVATE,
EntityType.LMS_SETUP, EntityType.LMS_SETUP,
new EntityKey(id, EntityType.LMS_SETUP))); new EntityKey(id, EntityType.LMS_SETUP)))
.getOrThrow();
} }
@RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE) @RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE)
@ -177,31 +191,29 @@ public class LmsSetupController {
return this.bulkActionService.createReport(new BulkAction( return this.bulkActionService.createReport(new BulkAction(
Type.HARD_DELETE, Type.HARD_DELETE,
EntityType.LMS_SETUP, 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( this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.LMS_SETUP, EntityType.LMS_SETUP,
type); type);
this.lmsSetupDAO.byId(id) this.lmsSetupDAO.byPK(lmsSetupId)
.flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity( .flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity(
institution, institution,
type)) type))
.getOrThrow(); .getOrThrow();
} }
private LmsSetup setActive(final Long id, final boolean active) { private EntityProcessingReport setActive(final Long id, final boolean active) {
checkPrivilegeForInstitution(id, PrivilegeType.MODIFY); checkPrivilegeForInstitution(id, PrivilegeType.MODIFY);
this.bulkActionService.doBulkAction(new BulkAction( return this.bulkActionService.createReport(new BulkAction(
(active) ? Type.ACTIVATE : Type.DEACTIVATE, (active) ? Type.ACTIVATE : Type.DEACTIVATE,
EntityType.LMS_SETUP, EntityType.LMS_SETUP,
new EntityKey(id, EntityType.LMS_SETUP))); new EntityKey(id, EntityType.LMS_SETUP)))
return this.lmsSetupDAO
.byId(id)
.getOrThrow(); .getOrThrow();
} }
@ -214,13 +226,14 @@ public class LmsSetupController {
return this.authorizationGrantService return this.authorizationGrantService
.checkGrantOnEntity(lmsSetup, privilegeType) .checkGrantOnEntity(lmsSetup, privilegeType)
.flatMap(this.lmsSetupDAO::save) .flatMap(this.lmsSetupDAO::save)
.flatMap(inst -> this.userActivityLogDAO.log(activityType, inst)); .flatMap(exam -> this.userActivityLogDAO.log(activityType, exam));
} }
private void checkBaseReadPrivilege() { private void checkReadPrivilege(final Long institutionId) {
this.authorizationGrantService.checkHasAnyPrivilege( this.authorizationGrantService.checkPrivilege(
EntityType.LMS_SETUP, EntityType.LMS_SETUP,
PrivilegeType.READ_ONLY); PrivilegeType.READ_ONLY,
institutionId);
} }
} }

View file

@ -74,19 +74,20 @@ public class QuizImportController {
@RequestParam(name = LMS_SETUP.ATTR_ID, required = true) final Long lmsSetupId, @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_NAME, required = false) final String nameLike,
@RequestParam(name = QuizData.FILTER_ATTR_START_TIME, required = false) final String startTime, @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 = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
@RequestParam(name = QuizData.PAGE_ATTR_SIZE, required = false) final Integer pageSize, @RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
@RequestParam(name = QuizData.PAGE_ATTR_SORT_BY, required = false) final String orderBy, @RequestParam(name = Page.ATTR_SORT_BY, required = false) final String orderBy,
@RequestParam(name = QuizData.PAGE_ATTR_SORT_ORDER, required = false) final String sortOrder) { @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final String sortOrder) {
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.EXAM,
PrivilegeType.READ_ONLY);
final LmsAPITemplate lmsAPITemplate = this.lmsAPIService final LmsAPITemplate lmsAPITemplate = this.lmsAPIService
.createLmsAPITemplate(lmsSetupId) .createLmsAPITemplate(lmsSetupId)
.getOrThrow(); .getOrThrow();
this.authorizationGrantService.checkPrivilege(
EntityType.EXAM,
PrivilegeType.READ_ONLY,
lmsAPITemplate.lmsSetup().institutionId);
return lmsAPITemplate.getQuizzesPage( return lmsAPITemplate.getQuizzesPage(
nameLike, nameLike,
Utils.dateTimeStringToTimestamp(startTime, null), Utils.dateTimeStringToTimestamp(startTime, null),

View file

@ -18,6 +18,8 @@ public class RestAPI {
public static final String ENDPOINT_QUIZ_IMPORT = "/quiz"; 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"; public static final String ENDPOINT_USER_ACTIVITY_LOG = "/useractivity";
} }

View file

@ -13,15 +13,14 @@ import java.util.Collection;
import javax.validation.Valid; import javax.validation.Valid;
import org.springframework.context.ApplicationEventPublisher; 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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; 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.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.user.UserFilter; 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.AuthorizationGrantService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType; 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.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.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
@ -45,11 +42,10 @@ import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint;
@WebServiceProfile @WebServiceProfile
@RestController @RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACCOUNT) @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_USER_ACCOUNT)
public class UserAccountController { public class UserAccountController extends ActivatableEntityController<UserInfo> {
private final UserDAO userDao; private final UserDAO userDao;
private final AuthorizationGrantService authorizationGrantService; private final AuthorizationGrantService authorizationGrantService;
private final UserService userService;
private final UserActivityLogDAO userActivityLogDAO; private final UserActivityLogDAO userActivityLogDAO;
private final PaginationService paginationService; private final PaginationService paginationService;
private final BulkActionService bulkActionService; private final BulkActionService bulkActionService;
@ -58,34 +54,40 @@ public class UserAccountController {
public UserAccountController( public UserAccountController(
final UserDAO userDao, final UserDAO userDao,
final AuthorizationGrantService authorizationGrantService, final AuthorizationGrantService authorizationGrantService,
final UserService userService,
final UserActivityLogDAO userActivityLogDAO, final UserActivityLogDAO userActivityLogDAO,
final PaginationService paginationService, final PaginationService paginationService,
final BulkActionService bulkActionService, final BulkActionService bulkActionService,
final ApplicationEventPublisher applicationEventPublisher) { final ApplicationEventPublisher applicationEventPublisher) {
super(authorizationGrantService, bulkActionService, userDao);
this.userDao = userDao; this.userDao = userDao;
this.authorizationGrantService = authorizationGrantService; this.authorizationGrantService = authorizationGrantService;
this.userService = userService;
this.userActivityLogDAO = userActivityLogDAO; this.userActivityLogDAO = userActivityLogDAO;
this.paginationService = paginationService; this.paginationService = paginationService;
this.bulkActionService = bulkActionService; this.bulkActionService = bulkActionService;
this.applicationEventPublisher = applicationEventPublisher; this.applicationEventPublisher = applicationEventPublisher;
} }
@InitBinder
public void initBinder(final WebDataBinder binder) throws Exception {
this.authorizationGrantService
.getUserService()
.addUsersInstitutionDefaultPropertySupport(binder);
}
@RequestMapping(method = RequestMethod.GET) @RequestMapping(method = RequestMethod.GET)
public Collection<UserInfo> getAll( public Collection<UserInfo> 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_ACTIVE, required = false) final Boolean active,
@RequestParam(name = UserFilter.FILTER_ATTR_NAME, required = false) final String name, @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_USER_NAME, required = false) final String username,
@RequestParam(name = UserFilter.FILTER_ATTR_EMAIL, required = false) final String email, @RequestParam(name = UserFilter.FILTER_ATTR_EMAIL, required = false) final String email,
@RequestParam(name = UserFilter.FILTER_ATTR_LOCALE, required = false) final String locale) { @RequestParam(name = UserFilter.FILTER_ATTR_LOCALE, required = false) final String locale) {
// fist check if current user has any privileges for this action checkReadPrivilege(institutionId);
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.USER,
PrivilegeType.READ_ONLY);
this.paginationService.setDefaultLimit(UserRecordDynamicSqlSupport.userRecord); this.paginationService.setDefaultLimit(UserRecordDynamicSqlSupport.userRecord);
return getAll(createUserFilter(institutionId, active, name, username, email, locale)); return getAll(createUserFilter(institutionId, active, name, username, email, locale));
@ -93,7 +95,10 @@ public class UserAccountController {
@RequestMapping(path = "/page", method = RequestMethod.GET) @RequestMapping(path = "/page", method = RequestMethod.GET)
public Page<UserInfo> getPage( public Page<UserInfo> 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_ACTIVE, required = false) final Boolean active,
@RequestParam(name = UserFilter.FILTER_ATTR_NAME, required = false) final String name, @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_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_BY, required = false) final String sortBy,
@RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) {
// fist check if current user has any privileges for this action checkReadPrivilege(institutionId);
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.USER,
PrivilegeType.READ_ONLY);
return this.paginationService.getPage( return this.paginationService.getPage(
pageNumber, pageNumber,
@ -121,21 +123,22 @@ public class UserAccountController {
@RequestMapping(path = "/me", method = RequestMethod.GET) @RequestMapping(path = "/me", method = RequestMethod.GET)
public UserInfo loggedInUser() { public UserInfo loggedInUser() {
return this.userService return this.authorizationGrantService
.getUserService()
.getCurrentUser() .getCurrentUser()
.getUserInfo(); .getUserInfo();
} }
@RequestMapping(path = "/{uuid}", method = RequestMethod.GET) // @Override
public UserInfo accountInfo(@PathVariable final String uuid) { // @RequestMapping(path = "/{uuid}", method = RequestMethod.GET)
return this.userDao // public UserInfo accountInfo(@PathVariable final String uuid) {
.byUuid(uuid) // return this.userDao
.flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity( // .byModelId(uuid)
userInfo, // .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity(
PrivilegeType.READ_ONLY)) // userInfo,
.getOrThrow(); // PrivilegeType.READ_ONLY))
// .getOrThrow();
} // }
@RequestMapping(path = "/create", method = RequestMethod.PUT) @RequestMapping(path = "/create", method = RequestMethod.PUT)
public UserInfo createUser(@Valid @RequestBody final UserMod userData) { public UserInfo createUser(@Valid @RequestBody final UserMod userData) {
@ -150,60 +153,52 @@ public class UserAccountController {
} }
@RequestMapping(path = "/{uuid}/activate", method = RequestMethod.POST) // @Override
public UserInfo activateUser(@PathVariable final String uuid) { // @RequestMapping(path = "/{uuid}/activate", method = RequestMethod.POST)
return setActive(uuid, true); // 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) // private void checkPrivilegeForUser(final String uuid, final PrivilegeType type) {
public UserInfo deactivateUser(@PathVariable final String uuid) { // this.authorizationGrantService.checkHasAnyPrivilege(
return setActive(uuid, false); // EntityType.USER,
} // type);
//
// this.userDao.byModelId(uuid)
// .flatMap(userInfo -> this.authorizationGrantService.checkGrantOnEntity(
// userInfo,
// type))
// .getOrThrow();
// }
@RequestMapping(path = "/{uuid}/delete", method = RequestMethod.DELETE) // private Result<EntityProcessingReport> setActive(final String uuid, final boolean active) {
public EntityProcessingReport deleteUser(@PathVariable final String uuid) { // this.checkPrivilegeForUser(uuid, PrivilegeType.MODIFY);
checkPrivilegeForUser(uuid, PrivilegeType.WRITE); //
// return this.bulkActionService.createReport(new BulkAction(
return this.bulkActionService.createReport(new BulkAction( // (active) ? Type.ACTIVATE : Type.DEACTIVATE,
Type.DEACTIVATE, // EntityType.USER,
EntityType.USER, // new EntityKey(uuid, EntityType.USER, false)));
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<UserInfo> _saveUser(final UserMod userData, final PrivilegeType privilegeType) { private Result<UserInfo> _saveUser(final UserMod userData, final PrivilegeType privilegeType) {
@ -261,4 +256,11 @@ public class UserAccountController {
: null; : null;
} }
private void checkReadPrivilege(final Long institutionId) {
this.authorizationGrantService.checkPrivilege(
EntityType.USER,
PrivilegeType.READ_ONLY,
institutionId);
}
} }

View file

@ -13,8 +13,11 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils; 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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; 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.Page;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; 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.UserActivityLogRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; 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.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationGrantService; 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.PrivilegeType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
@WebServiceProfile @WebServiceProfile
@ -52,84 +58,113 @@ public class UserActivityLogController {
this.paginationService = paginationService; this.paginationService = paginationService;
} }
@InitBinder
public void initBinder(final WebDataBinder binder) throws Exception {
this.authorizationGrantService
.getUserService()
.addUsersInstitutionDefaultPropertySupport(binder);
}
@RequestMapping(method = RequestMethod.GET) @RequestMapping(method = RequestMethod.GET)
public Collection<UserActivityLog> getAll( public Collection<UserActivityLog> getAll(
@RequestParam(required = false) final Long from, @RequestParam(
@RequestParam(required = false) final Long to, name = UserActivityLog.FILTER_ATTR_INSTITUTION,
@RequestParam(required = false) final String activityTypes, required = true,
@RequestParam(required = false) final String entityTypes) { 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); 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) @RequestMapping(path = "/{userId}", method = RequestMethod.GET)
public Collection<UserActivityLog> getAllForUser( public Collection<UserActivityLog> getAllForUser(
@PathVariable final String userId, @PathVariable final String userId,
@RequestParam(required = false) final Long from, @RequestParam(
@RequestParam(required = false) final Long to, name = UserActivityLog.FILTER_ATTR_INSTITUTION,
@RequestParam(required = false) final String activityTypes, required = true,
@RequestParam(required = false) final String entityTypes) { 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); 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) @RequestMapping(path = "/page", method = RequestMethod.GET)
public Page<UserActivityLog> getPage( public Page<UserActivityLog> getPage(
@RequestParam(required = false) final Long from, @RequestParam(
@RequestParam(required = false) final Long to, name = UserActivityLog.FILTER_ATTR_INSTITUTION,
@RequestParam(required = false) final String activityTypes, required = true,
@RequestParam(required = false) final String entityTypes, 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_NUMBER, required = false) final Integer pageNumber,
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, @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_BY, required = false) final String sortBy,
@RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) {
checkBaseReadPrivilege(); checkBaseReadPrivilege(institutionId);
return this.paginationService.getPage( return this.paginationService.getPage(
pageNumber, pageNumber,
pageSize, pageSize,
sortBy, sortBy,
sortOrder, sortOrder,
UserRecordDynamicSqlSupport.userRecord, UserRecordDynamicSqlSupport.userRecord,
() -> _getAll(null, from, to, activityTypes, entityTypes)); () -> _getAll(institutionId, null, from, to, activityTypes, entityTypes));
} }
@RequestMapping(path = "/page/{userId}", method = RequestMethod.GET) @RequestMapping(path = "/page/{userId}", method = RequestMethod.GET)
public Page<UserActivityLog> getPageForUser( public Page<UserActivityLog> getPageForUser(
@PathVariable final String userId, @PathVariable final String userId,
@RequestParam(required = false) final Long from, @RequestParam(
@RequestParam(required = false) final Long to, name = UserActivityLog.FILTER_ATTR_INSTITUTION,
@RequestParam(required = false) final String activityTypes, required = true,
@RequestParam(required = false) final String entityTypes, 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_NUMBER, required = false) final Integer pageNumber,
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize, @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_BY, required = false) final String sortBy,
@RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) { @RequestParam(name = Page.ATTR_SORT_ORDER, required = false) final Page.SortOrder sortOrder) {
checkBaseReadPrivilege(); checkBaseReadPrivilege(institutionId);
return this.paginationService.getPage( return this.paginationService.getPage(
pageNumber, pageNumber,
pageSize, pageSize,
sortBy, sortBy,
sortOrder, sortOrder,
UserRecordDynamicSqlSupport.userRecord, UserRecordDynamicSqlSupport.userRecord,
() -> _getAll(userId, from, to, activityTypes, entityTypes)); () -> _getAll(institutionId, userId, from, to, activityTypes, entityTypes));
}
private void checkBaseReadPrivilege() {
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.USER_ACTIVITY_LOG,
PrivilegeType.READ_ONLY);
} }
private Collection<UserActivityLog> _getAll( private Collection<UserActivityLog> _getAll(
final Long institutionId,
final String userId, final String userId,
final Long from, final String from,
final Long to, final String to,
final String activityTypes, final String activityTypes,
final String entityTypes) { final String entityTypes) {
@ -142,24 +177,32 @@ public class UserActivityLogController {
Arrays.asList(StringUtils.split(entityTypes, Constants.LIST_SEPARATOR)))) Arrays.asList(StringUtils.split(entityTypes, Constants.LIST_SEPARATOR))))
: null; : null;
if (_activityTypes != null || _entityTypes != null) { final Predicate<UserActivityLogRecord> 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 -> { return true;
if (_activityTypes != null && !_activityTypes.contains(record.getActivityType())) {
return false;
}
if (_entityTypes != null && !_entityTypes.contains(record.getEntityType())) {
return false;
} }
: record -> true;
return true; return this.userActivityLogDAO.all(
}).getOrThrow(); institutionId,
userId,
Utils.toMilliSeconds(from),
Utils.toMilliSeconds(to),
filter).getOrThrow();
}
} else { private void checkBaseReadPrivilege(final Long institutionId) {
this.authorizationGrantService.checkPrivilege(
return this.userActivityLogDAO.all(userId, from, to, record -> true) EntityType.USER_ACTIVITY_LOG,
.getOrThrow(); PrivilegeType.READ_ONLY,
} institutionId);
} }
} }

View file

@ -99,4 +99,12 @@ public abstract class AdministrationAPIIntegrationTest {
return obtainAccessToken("examAdmin1", "admin"); return obtainAccessToken("examAdmin1", "admin");
} }
// protected static class TestHelper {
//
// private Supplier<String> accessTokenSuplier;
// private String query;
// private String endpoint;
// private Object
// }
} }

View file

@ -21,6 +21,7 @@ import java.util.NoSuchElementException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import org.junit.Test; import org.junit.Test;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -28,7 +29,10 @@ import org.springframework.test.context.jdbc.Sql;
import com.fasterxml.jackson.core.type.TypeReference; 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.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.Page;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog; import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
@ -113,6 +117,15 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
contentAsString); 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 @Test
public void getAllUserInfoNoFilter() throws Exception { public void getAllUserInfoNoFilter() throws Exception {
String token = getSebAdminAccess(); String token = getSebAdminAccess();
@ -126,34 +139,65 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
// expecting all users for a SEBAdmin except inactive. // expecting all users for a SEBAdmin except inactive.
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.size() == 7); assertTrue(userInfos.size() == 3);
assertNotNull(getUserInfo("admin", userInfos)); assertNotNull(getUserInfo("admin", userInfos));
assertNotNull(getUserInfo("inst1Admin", userInfos)); assertNotNull(getUserInfo("inst1Admin", userInfos));
assertNotNull(getUserInfo("examSupporter", 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( 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<List<UserInfo>>() {
});
// 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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
new TypeReference<List<UserInfo>>() { new TypeReference<List<UserInfo>>() {
}); });
// expecting all users of institution 1 for Institutional Admin of institution 1
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.size() == 3); assertTrue(userInfos.size() == 3);
assertNotNull(getUserInfo("admin", userInfos)); assertNotNull(getUserInfo("inst2Admin", userInfos));
assertNotNull(getUserInfo("inst1Admin", userInfos)); assertNotNull(getUserInfo("examAdmin1", userInfos));
assertNotNull(getUserInfo("examSupporter", 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<List<UserInfo>>() {
});
assertNotNull(userInfos);
assertTrue(userInfos.size() == 1);
assertNotNull(getUserInfo("deactivatedUser", userInfos));
} }
@Test @Test
public void getPageNoFilterNoPageAttributes() throws Exception { public void getPageNoFilterNoPageAttributes() throws Exception {
// expecting all user accounts of the institution of SEBAdmin
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue( final Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page") this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page")
@ -166,15 +210,18 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 1); assertTrue(userInfos.numberOfPages == 1);
assertNotNull(userInfos.content); assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 7); assertTrue(userInfos.content.size() == 3);
assertEquals("[user1, user2, user3, user4, user5, user6, user7]", getOrderedUUIDs(userInfos.content)); assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content));
} }
@Test @Test
public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception { public void getPageNoFilterNoPageAttributesFromOtherInstitution() throws Exception {
// expecting all user accounts of institution 2
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
final Page<UserInfo> userInfos = this.jsonMapper.readValue( final Page<UserInfo> 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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -184,8 +231,26 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 1); assertTrue(userInfos.numberOfPages == 1);
assertNotNull(userInfos.content); assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 7); assertTrue(userInfos.content.size() == 4);
assertEquals("[user7, user6, user5, user4, user3, user2, user1]", getOrderedUUIDs(userInfos.content)); assertEquals("[user3, user4, user6, user7]", getOrderedUUIDs(userInfos.content));
}
@Test
public void getPageNoFilterNoPageAttributesDescendingOrder() throws Exception {
final String token = getSebAdminAccess();
final Page<UserInfo> 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<Page<UserInfo>>() {
});
assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 1);
assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 3);
assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content));
} }
@Test @Test
@ -195,24 +260,25 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
// first page default sort order // first page default sort order
Page<UserInfo> userInfos = this.jsonMapper.readValue( Page<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/page?pageNumber=1&pageSize=3") .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT
.header("Authorization", "Bearer " + token)) + "/page?page_number=1&page_size=3&institution=2")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
new TypeReference<Page<UserInfo>>() { new TypeReference<Page<UserInfo>>() {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 3); assertTrue(userInfos.numberOfPages == 2);
assertNotNull(userInfos.content); assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 3); assertTrue(userInfos.content.size() == 3);
assertEquals("[user1, user2, user3]", getOrderedUUIDs(userInfos.content)); assertEquals("[user3, user4, user6]", getOrderedUUIDs(userInfos.content));
// second page default sort order // second page default sort order
userInfos = this.jsonMapper.readValue( userInfos = this.jsonMapper.readValue(
this.mockMvc this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT .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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -220,16 +286,17 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 3); assertTrue(userInfos.numberOfPages == 2);
assertNotNull(userInfos.content); assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 3); assertTrue(userInfos.pageSize == 1);
assertEquals("[user4, user5, user6]", getOrderedUUIDs(userInfos.content)); 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( userInfos = this.jsonMapper.readValue(
this.mockMvc this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT .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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -237,7 +304,8 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 3); assertTrue(userInfos.numberOfPages == 2);
assertTrue(userInfos.pageNumber == 2);
assertNotNull(userInfos.content); assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 1); assertTrue(userInfos.content.size() == 1);
assertEquals("[user7]", getOrderedUUIDs(userInfos.content)); assertEquals("[user7]", getOrderedUUIDs(userInfos.content));
@ -246,7 +314,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
userInfos = this.jsonMapper.readValue( userInfos = this.jsonMapper.readValue(
this.mockMvc this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT .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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -254,10 +322,10 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.numberOfPages == 3); assertTrue(userInfos.numberOfPages == 2);
assertNotNull(userInfos.content); assertNotNull(userInfos.content);
assertTrue(userInfos.content.size() == 3); assertTrue(userInfos.content.size() == 3);
assertEquals("[user7, user6, user5]", getOrderedUUIDs(userInfos.content)); assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content));
} }
private String getOrderedUUIDs(final Collection<UserInfo> list) { private String getOrderedUUIDs(final Collection<UserInfo> list) {
@ -280,15 +348,14 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.size() == 7); assertTrue(userInfos.size() == 3);
assertNotNull(getUserInfo("deactivatedUser", userInfos));
} }
@Test @Test
public void getAllUserInfoWithOnlyActice() throws Exception { public void getAllUserInfoWithOnlyActive() throws Exception {
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
final List<UserInfo> userInfos = this.jsonMapper.readValue( final List<UserInfo> 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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -296,14 +363,16 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.size() == 6); assertTrue(userInfos.size() == 3);
assertNull(getUserInfo("deactivatedUser", userInfos)); assertNull(getUserInfo("deactivatedUser", userInfos));
} }
@Test @Test
public void getAllUserInfoOnlyInactive() throws Exception { public void getAllUserInfoOnlyInactive() throws Exception {
// expecting none for SEBAdmins institution
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
final List<UserInfo> userInfos = this.jsonMapper.readValue( List<UserInfo> userInfos = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false") this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "?active=false")
.header("Authorization", "Bearer " + token)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
@ -311,6 +380,18 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
new TypeReference<List<UserInfo>>() { new TypeReference<List<UserInfo>>() {
}); });
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<List<UserInfo>>() {
});
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.size() == 1); assertTrue(userInfos.size() == 1);
assertNotNull(getUserInfo("deactivatedUser", userInfos)); assertNotNull(getUserInfo("deactivatedUser", userInfos));
@ -328,8 +409,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(userInfos); assertNotNull(userInfos);
assertTrue(userInfos.size() == 2); assertTrue(userInfos.size() == 1);
assertNotNull(getUserInfo("examAdmin1", userInfos));
assertNotNull(getUserInfo("examSupporter", userInfos)); assertNotNull(getUserInfo("examSupporter", userInfos));
} }
@ -381,8 +461,9 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
// check user activity log for newly created user // check user activity log for newly created user
final List<UserActivityLog> logs = this.jsonMapper.readValue( final List<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc this.mockMvc
.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user1?activityTypes=CREATE") .perform(
.header("Authorization", "Bearer " + token)) get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user1?activity_types=CREATE")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
new TypeReference<List<UserActivityLog>>() { new TypeReference<List<UserActivityLog>>() {
@ -700,7 +781,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
@Test @Test
public void deactivateUserAccount() throws Exception { 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 // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account
final String examAdminToken = getExamAdmin1(); final String examAdminToken = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/deactivate") 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 // With SEB Administrator it should work
final String sebAdminToken = getSebAdminAccess(); 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") this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user4/deactivate")
.header("Authorization", "Bearer " + sebAdminToken)) .header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
new TypeReference<EntityProcessingReport>() {
});
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<UserInfo>() { new TypeReference<UserInfo>() {
}); });
assertNotNull(deactivatedUser); assertNotNull(user);
assertFalse(deactivatedUser.isActive()); assertFalse(user.isActive());
// check also user activity log // check also user activity log
final Collection<UserActivityLog> userLogs = this.jsonMapper.readValue( final Collection<UserActivityLog> userLogs = this.jsonMapper.readValue(
@ -738,7 +835,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTest {
@Test @Test
public void activateUserAccount() throws Exception { 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 // only a SEB Administrator or an Institutional administrator should be able to deactivate a user-account
final String examAdminToken = getExamAdmin1(); final String examAdminToken = getExamAdmin1();
this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/activate") 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 // With SEB Administrator it should work
final String sebAdminToken = getSebAdminAccess(); 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") this.mockMvc.perform(post(this.endpoint + RestAPI.ENDPOINT_USER_ACCOUNT + "/user6/activate")
.header("Authorization", "Bearer " + sebAdminToken)) .header("Authorization", "Bearer " + sebAdminToken))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
new TypeReference<EntityProcessingReport>() {
});
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<UserInfo>() { new TypeReference<UserInfo>() {
}); });
assertNotNull(activatedUser); assertNotNull(user);
assertTrue(activatedUser.isActive()); assertTrue(user.isActive());
// check also user activity log // check also user activity log
final Collection<UserActivityLog> userLogs = this.jsonMapper.readValue( final Collection<UserActivityLog> userLogs = this.jsonMapper.readValue(

View file

@ -8,18 +8,19 @@
package ch.ethz.seb.sebserver.webservice.integration.api; package ch.ethz.seb.sebserver.webservice.integration.api;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.List; import java.util.List;
import org.joda.time.DateTime;
import org.junit.Test; import org.junit.Test;
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql;
import com.fasterxml.jackson.core.type.TypeReference; 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.gbl.model.user.UserActivityLog;
import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI; import ch.ethz.seb.sebserver.webservice.weblayer.api.RestAPI;
@ -38,14 +39,15 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(logs); assertNotNull(logs);
assertTrue(5 == logs.size()); assertTrue(2 == logs.size());
} }
@Test @Test
public void getAllAsSEBAdminForUser() throws Exception { public void getAllAsSEBAdminForUser() throws Exception {
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
// for a user in another institution, the institution has to be defined
List<UserActivityLog> logs = this.jsonMapper.readValue( List<UserActivityLog> 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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -55,6 +57,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
assertNotNull(logs); assertNotNull(logs);
assertTrue(2 == logs.size()); assertTrue(2 == logs.size());
// for a user in the same institution no institution is needed
logs = this.jsonMapper.readValue( logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user2") this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "/user2")
.header("Authorization", "Bearer " + token)) .header("Authorization", "Bearer " + token))
@ -69,47 +72,18 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
@Test @Test
public void getAllAsSEBAdminInTimeRange() throws Exception { 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(); final String token = getSebAdminAccess();
List<UserActivityLog> logs = this.jsonMapper.readValue( List<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?from=2") this.mockMvc.perform(
.header("Authorization", "Bearer " + token)) get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institution=2&from=" + sec2)
.andExpect(status().isOk()) .header("Authorization", "Bearer " + token))
.andReturn().getResponse().getContentAsString(),
new TypeReference<List<UserActivityLog>>() {
});
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<List<UserActivityLog>>() {
});
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<List<UserActivityLog>>() {
});
assertNotNull(logs);
assertTrue(2 == logs.size());
}
@Test
public void getAllAsSEBAdminForActivityType() throws Exception {
final String token = getSebAdminAccess();
List<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc.perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activityTypes=CREATE")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
new TypeReference<List<UserActivityLog>>() { new TypeReference<List<UserActivityLog>>() {
@ -120,8 +94,8 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
logs = this.jsonMapper.readValue( logs = this.jsonMapper.readValue(
this.mockMvc this.mockMvc
.perform( .perform(get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?institution=2&from="
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG + "?activityTypes=CREATE,MODIFY") + sec2 + "&to=" + sec4)
.header("Authorization", "Bearer " + token)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -129,7 +103,77 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(logs); 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<List<UserActivityLog>>() {
});
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<List<UserActivityLog>>() {
});
assertNotNull(logs);
assertTrue(3 == logs.size());
}
@Test
public void getAllAsSEBAdminForActivityType() throws Exception {
final String token = getSebAdminAccess();
List<UserActivityLog> 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<List<UserActivityLog>>() {
});
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<List<UserActivityLog>>() {
});
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<List<UserActivityLog>>() {
});
assertNotNull(logs);
assertTrue(3 == logs.size());
} }
@Test @Test
@ -137,7 +181,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
final String token = getSebAdminAccess(); final String token = getSebAdminAccess();
List<UserActivityLog> logs = this.jsonMapper.readValue( List<UserActivityLog> logs = this.jsonMapper.readValue(
this.mockMvc 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)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -151,7 +195,7 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
this.mockMvc this.mockMvc
.perform( .perform(
get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG get(this.endpoint + RestAPI.ENDPOINT_USER_ACTIVITY_LOG
+ "?entityTypes=INSTITUTION,EXAM") + "?entity_types=INSTITUTION,EXAM")
.header("Authorization", "Bearer " + token)) .header("Authorization", "Bearer " + token))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(), .andReturn().getResponse().getContentAsString(),
@ -159,7 +203,21 @@ public class UserActivityLogAPITest extends AdministrationAPIIntegrationTest {
}); });
assertNotNull(logs); 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<List<UserActivityLog>>() {
});
assertNotNull(logs);
assertTrue(3 == logs.size());
} }
@Test @Test

View file

@ -24,10 +24,10 @@ INSERT INTO user_role VALUES
; ;
INSERT INTO user_activity_log VALUES INSERT INTO user_activity_log VALUES
(1, 'user1', 1, 'MODIFY', 'INSTITUTION', '1', 'some message'), (1, 'user1', 1000, 'MODIFY', 'INSTITUTION', '1', 'some message'),
(2, 'user2', 2, 'CREATE', 'EXAM', '1', 'some message'), (2, 'user2', 2000, 'CREATE', 'EXAM', '1', 'some message'),
(3, 'user3', 3, 'CREATE', 'EXAM', '2', 'some message'), (3, 'user3', 3000, 'CREATE', 'EXAM', '2', 'some message'),
(4, 'user4', 4, 'CREATE', 'EXAM', '33', 'some message'), (4, 'user4', 4000, 'CREATE', 'EXAM', '33', 'some message'),
(5, 'user4', 5, 'MODIFY', 'EXAM', '33', 'some message') (5, 'user4', 5000, 'MODIFY', 'EXAM', '33', 'some message')
; ;