diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index 3d14ba06..4a1bc896 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -59,6 +59,18 @@ public final class API { public static final String EXAM_INDICATOR_ENDPOINT = "/indicator"; + public static final String CONFIGURATION_NODE_ENDPOINT = "/configuration_node"; + + public static final String CONFIGURATION_ENDPOINT = "/configuration"; + + public static final String CONFIGURATION_VALUE_ENDPOINT = "/configuration_value"; + + public static final String CONFIGURATION_ATTRIBUTE_ENDPOINT = "/configuration_attribute"; + + public static final String ORIENTATION_ENDPOINT = "/orientation"; + + public static final String EXAM_CONFIGURATION_MAP_ENDPOINT = "/exam_configuration_map"; + public static final String USER_ACTIVITY_LOG_ENDPOINT = "/useractivity"; public static final String SELF_PATH_SEGMENT = "/self"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java index be4c95e7..42c1b275 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Indicator.java @@ -21,14 +21,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; -import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM; import ch.ethz.seb.sebserver.gbl.model.Domain.INDICATOR; import ch.ethz.seb.sebserver.gbl.model.Domain.THRESHOLD; +import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.util.Utils; -import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; @JsonIgnoreProperties(ignoreUnknown = true) -public final class Indicator implements GrantEntity { +public final class Indicator implements Entity { public static final String FILTER_ATTR_EXAM = "exam"; @@ -40,10 +39,6 @@ public final class Indicator implements GrantEntity { @JsonProperty(INDICATOR.ATTR_ID) public final Long id; - @JsonProperty(EXAM.ATTR_INSTITUTION_ID) - @NotNull - public final Long institutionId; - @JsonProperty(INDICATOR.ATTR_EXAM_ID) @NotNull public final Long examId; @@ -63,24 +58,17 @@ public final class Indicator implements GrantEntity { @JsonProperty(THRESHOLD.REFERENCE_NAME) public final List thresholds; - @JsonProperty(EXAM.ATTR_OWNER) - public final String examOwner; - @JsonCreator public Indicator( @JsonProperty(INDICATOR.ATTR_ID) final Long id, - @JsonProperty(EXAM.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(INDICATOR.ATTR_EXAM_ID) final Long examId, - @JsonProperty(EXAM.ATTR_OWNER) final String examOwner, @JsonProperty(INDICATOR.ATTR_NAME) final String name, @JsonProperty(INDICATOR.ATTR_TYPE) final IndicatorType type, @JsonProperty(INDICATOR.ATTR_COLOR) final String defaultColor, @JsonProperty(THRESHOLD.REFERENCE_NAME) final Collection thresholds) { this.id = id; - this.institutionId = institutionId; this.examId = examId; - this.examOwner = examOwner; this.name = name; this.type = type; this.defaultColor = defaultColor; @@ -89,9 +77,7 @@ public final class Indicator implements GrantEntity { public Indicator(final Exam exam, final POSTMapper postParams) { this.id = null; - this.institutionId = exam.institutionId; this.examId = exam.id; - this.examOwner = exam.owner; this.name = postParams.getString(Domain.INDICATOR.ATTR_NAME); this.type = postParams.getEnum(Domain.INDICATOR.ATTR_TYPE, IndicatorType.class); this.defaultColor = postParams.getString(Domain.INDICATOR.ATTR_COLOR); @@ -117,17 +103,6 @@ public final class Indicator implements GrantEntity { return this.id; } - @Override - - public Long getInstitutionId() { - return this.institutionId; - } - - @Override - public String getOwnerId() { - return this.examOwner; - } - public Long getExamId() { return this.examId; } @@ -151,8 +126,8 @@ public final class Indicator implements GrantEntity { + this.defaultColor + ", thresholds=" + this.thresholds + "]"; } - public static Indicator createNew(final Long institutionId, final Exam exam) { - return new Indicator(null, institutionId, exam.id, exam.owner, null, null, null, null); + public static Indicator createNew(final Exam exam) { + return new Indicator(null, exam.id, null, null, null, null); } public static final class Threshold { diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationAttribute.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationAttribute.java index 31b1cf59..71997f45 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationAttribute.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/ConfigurationAttribute.java @@ -16,6 +16,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain.CONFIGURATION_ATTRIBUTE; import ch.ethz.seb.sebserver.gbl.model.Entity; @@ -73,6 +75,17 @@ public final class ConfigurationAttribute implements Entity { this.defaultValue = defaultValue; } + public ConfigurationAttribute(final POSTMapper postParams) { + this.id = null; + this.parentId = postParams.getLong(Domain.CONFIGURATION_ATTRIBUTE.ATTR_PARENT_ID); + this.name = postParams.getString(Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME); + this.type = postParams.getEnum(Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE, AttributeType.class); + this.resources = postParams.getString(Domain.CONFIGURATION_ATTRIBUTE.ATTR_RESOURCES); + this.validator = postParams.getString(Domain.CONFIGURATION_ATTRIBUTE.ATTR_VALIDATOR); + this.dependencies = postParams.getString(Domain.CONFIGURATION_ATTRIBUTE.ATTR_DEPENDENCIES); + this.defaultValue = postParams.getString(Domain.CONFIGURATION_ATTRIBUTE.ATTR_DEFAULT_VALUE); + } + @Override public String getModelId() { return (this.id != null) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Orientation.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Orientation.java index 98cfe059..e05c1376 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Orientation.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/sebconfig/Orientation.java @@ -15,6 +15,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain.ORIENTATION; import ch.ethz.seb.sebserver.gbl.model.Entity; @@ -76,6 +78,18 @@ public final class Orientation implements Entity { this.height = height; } + public Orientation(final ConfigurationAttribute attr, final POSTMapper postParams) { + this.id = null; + this.attributeId = attr.id; + this.template = postParams.getString(Domain.ORIENTATION.ATTR_TEMPLATE); + this.view = postParams.getString(Domain.ORIENTATION.ATTR_VIEW); + this.group = postParams.getString(Domain.ORIENTATION.ATTR_GROUP); + this.xPosition = postParams.getInteger(Domain.ORIENTATION.ATTR_X_POSITION); + this.yPosition = postParams.getInteger(Domain.ORIENTATION.ATTR_Y_POSITION); + this.width = postParams.getInteger(Domain.ORIENTATION.ATTR_WIDTH); + this.height = postParams.getInteger(Domain.ORIENTATION.ATTR_HEIGHT); + } + @Override public String getModelId() { return (this.id != null) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java index b708255b..b8afe09a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/IndicatorForm.java @@ -81,7 +81,7 @@ public class IndicatorForm implements TemplateComposer { // get data or create new. Handle error if happen final Indicator indicator = (isNew) - ? Indicator.createNew(exam.getInstitutionId(), exam) + ? Indicator.createNew(exam) : restService .getBuilder(GetIndicator.class) .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationServiceImpl.java index c4caca1d..33a24fd6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/AuthorizationServiceImpl.java @@ -105,20 +105,42 @@ public class AuthorizationServiceImpl implements AuthorizationService { .withInstitutionalPrivilege(PrivilegeType.READ) .andForRole(UserRole.EXAM_ADMIN) .withInstitutionalPrivilege(PrivilegeType.WRITE) - .withOwnerPrivilege(PrivilegeType.WRITE) .andForRole(UserRole.EXAM_SUPPORTER) .withInstitutionalPrivilege(PrivilegeType.READ) .create(); - // grants for indicators - addPrivilege(EntityType.INDICATOR) + // grants for configuration node + addPrivilege(EntityType.CONFIGURATION_NODE) .forRole(UserRole.SEB_SERVER_ADMIN) .withBasePrivilege(PrivilegeType.READ) .andForRole(UserRole.INSTITUTIONAL_ADMIN) .withInstitutionalPrivilege(PrivilegeType.READ) .andForRole(UserRole.EXAM_ADMIN) .withInstitutionalPrivilege(PrivilegeType.WRITE) - .withOwnerPrivilege(PrivilegeType.WRITE) + .andForRole(UserRole.EXAM_SUPPORTER) + .withInstitutionalPrivilege(PrivilegeType.MODIFY) + .create(); + + // grants for configuration attributes + addPrivilege(EntityType.CONFIGURATION_ATTRIBUTE) + .forRole(UserRole.SEB_SERVER_ADMIN) + .withBasePrivilege(PrivilegeType.WRITE) + .andForRole(UserRole.INSTITUTIONAL_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.READ) + .andForRole(UserRole.EXAM_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.READ) + .andForRole(UserRole.EXAM_SUPPORTER) + .withInstitutionalPrivilege(PrivilegeType.READ) + .create(); + + // grants for configuration orientations + addPrivilege(EntityType.ORIENTATION) + .forRole(UserRole.SEB_SERVER_ADMIN) + .withBasePrivilege(PrivilegeType.WRITE) + .andForRole(UserRole.INSTITUTIONAL_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.READ) + .andForRole(UserRole.EXAM_ADMIN) + .withInstitutionalPrivilege(PrivilegeType.READ) .andForRole(UserRole.EXAM_SUPPORTER) .withInstitutionalPrivilege(PrivilegeType.READ) .create(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java index 49adbb64..32edf1ed 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java @@ -34,12 +34,10 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold; 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.datalayer.batis.mapper.ExamRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ThresholdRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ThresholdRecordMapper; -import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.IndicatorRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ThresholdRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkAction; @@ -56,16 +54,13 @@ public class IndicatorDAOImpl implements IndicatorDAO { private final IndicatorRecordMapper indicatorRecordMapper; private final ThresholdRecordMapper thresholdRecordMapper; - private final ExamRecordMapper examRecordMapper; public IndicatorDAOImpl( final IndicatorRecordMapper indicatorRecordMapper, - final ThresholdRecordMapper thresholdRecordMapper, - final ExamRecordMapper examRecordMapper) { + final ThresholdRecordMapper thresholdRecordMapper) { this.indicatorRecordMapper = indicatorRecordMapper; this.thresholdRecordMapper = thresholdRecordMapper; - this.examRecordMapper = examRecordMapper; } @Override @@ -175,6 +170,7 @@ public class IndicatorDAOImpl implements IndicatorDAO { // insert thresholds modified.thresholds .stream() + .filter(threshold -> threshold.value != null && threshold.color != null) .map(threshold -> new ThresholdRecord( null, newRecord.getId(), @@ -321,8 +317,6 @@ public class IndicatorDAOImpl implements IndicatorDAO { private Result toDomainModel(final IndicatorRecord record) { return Result.tryCatch(() -> { - final ExamRecord examRecord = this.examRecordMapper.selectByPrimaryKey(record.getExamId()); - final List thresholds = this.thresholdRecordMapper.selectByExample() .where(ThresholdRecordDynamicSqlSupport.indicatorId, isEqualTo(record.getId())) .build() @@ -335,9 +329,7 @@ public class IndicatorDAOImpl implements IndicatorDAO { return new Indicator( record.getId(), - examRecord.getInstitutionId(), record.getExamId(), - examRecord.getOwner(), record.getName(), IndicatorType.valueOf(record.getType()), record.getColor(), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationAttributeController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationAttributeController.java new file mode 100644 index 00000000..14fa52bf --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationAttributeController.java @@ -0,0 +1,71 @@ +/* + * 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.mybatis.dynamic.sql.SqlTable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +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.ConfigurationAttributeRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; + +@WebServiceProfile +@RestController +@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.CONFIGURATION_ATTRIBUTE_ENDPOINT) +public class ConfigurationAttributeController extends EntityController { + + protected ConfigurationAttributeController( + final AuthorizationService authorization, + final BulkActionService bulkActionService, + final ConfigurationAttributeDAO entityDAO, + final UserActivityLogDAO userActivityLogDAO, + final PaginationService paginationService, + final BeanValidationService beanValidationService) { + + super(authorization, + bulkActionService, + entityDAO, + userActivityLogDAO, + paginationService, + beanValidationService); + } + + @Override + protected ConfigurationAttribute createNew(final POSTMapper postParams) { + // TODO Auto-generated method stub + return new ConfigurationAttribute(postParams); + } + + @Override + protected SqlTable getSQLTableOfEntity() { + return ConfigurationAttributeRecordDynamicSqlSupport.configurationAttributeRecord; + } + + @Override + protected Result checkCreateAccess(final ConfigurationAttribute entity) { + return Result.of(entity); // Skips the entity based grant check + } + + @Override + protected GrantEntity toGrantEntity(final ConfigurationAttribute entity) { + return null; // Skips the entity based grant check + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java index d40f21df..57675db3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -135,7 +135,7 @@ public abstract class EntityController { // if current user has no read access for specified entity type within other institution // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance - if (!this.authorization.hasGrant(PrivilegeType.READ, this.entityDAO.entityType())) { + if (!this.authorization.hasGrant(PrivilegeType.READ, getGrantEntityType())) { filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); } @@ -170,7 +170,7 @@ public abstract class EntityController { // if current user has no read access for specified entity type within other institution then its own institution, // then the current users institutionId is put as a SQL filter criteria attribute to extends query performance - if (!this.authorization.hasGrant(PrivilegeType.READ, this.entityDAO.entityType())) { + if (!this.authorization.hasGrant(PrivilegeType.READ, this.getGrantEntityType())) { filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); } @@ -266,10 +266,7 @@ public abstract class EntityController { defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId) { // check modify privilege for requested institution and concrete entityType - this.authorization.check( - PrivilegeType.MODIFY, - this.entityDAO.entityType(), - institutionId); + this.checkModifyPrivilege(institutionId); final POSTMapper postMap = new POSTMapper(allRequestParams) .putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId)); @@ -323,10 +320,23 @@ public abstract class EntityController { .getOrThrow(); } + protected void checkReadPrivilege() { + this.authorization.check( + PrivilegeType.READ, + getGrantEntityType()); + } + protected void checkReadPrivilege(final Long institutionId) { this.authorization.check( PrivilegeType.READ, - this.entityDAO.entityType(), + getGrantEntityType(), + institutionId); + } + + protected void checkModifyPrivilege(final Long institutionId) { + this.authorization.check( + PrivilegeType.MODIFY, + getGrantEntityType(), institutionId); } @@ -361,7 +371,7 @@ public abstract class EntityController { return Result.of(entity); } - private Result checkReadAccess(final T entity) { + protected Result checkReadAccess(final T entity) { final GrantEntity grantEntity = toGrantEntity(entity); if (grantEntity != null) { this.authorization.checkRead(grantEntity); @@ -369,7 +379,7 @@ public abstract class EntityController { return Result.of(entity); } - private boolean hasReadAccess(final T entity) { + protected boolean hasReadAccess(final T entity) { final GrantEntity grantEntity = toGrantEntity(entity); if (grantEntity != null) { return this.authorization.hasReadonlyGrant(grantEntity); @@ -378,7 +388,7 @@ public abstract class EntityController { return true; } - private Result checkModifyAccess(final T entity) { + protected Result checkModifyAccess(final T entity) { final GrantEntity grantEntity = toGrantEntity(entity); if (grantEntity != null) { this.authorization.checkModify(grantEntity); @@ -386,7 +396,7 @@ public abstract class EntityController { return Result.of(entity); } - private Result checkWriteAccess(final T entity) { + protected Result checkWriteAccess(final T entity) { final GrantEntity grantEntity = toGrantEntity(entity); if (grantEntity != null) { this.authorization.checkWrite(grantEntity); @@ -394,15 +404,37 @@ public abstract class EntityController { return Result.of(entity); } - private Result checkCreateAccess(final M entity) { - final GrantEntity grantEntity = toGrantEntity(entity); - if (grantEntity != null) { - this.authorization.checkWrite(grantEntity); + /** Checks creation (write) privileges for a given Entity. + * Usually the GrantEntity and the Entity instance are the same if the Entity extends from GrantEntity. + * Otherwise the implementing EntityController must override this method and resolve the + * related GrantEntity for a given Entity. + * For example, the GrantEntity of Indicator is the related Exam + * + * @param entity the Entity to check creation/write access for + * @return Result of the access check containing either the original entity or an error if no access granted */ + protected Result checkCreateAccess(final M entity) { + if (entity instanceof GrantEntity) { + this.authorization.checkWrite((GrantEntity) entity); + return Result.of(entity); } - return Result.of(entity); + + return Result.ofError(new IllegalArgumentException("Entity instance is not of type GrantEntity. " + + "Do override the checkCreateAccess method from EntityController within the specific -Controller implementation")); } - protected GrantEntity toGrantEntity(final Entity entity) { + /** Gets the GrantEntity instance for a given Entity instance. + * Usually the GrantEntity and the Entity instance are the same if the Entity extends from GrantEntity. + * Otherwise the implementing EntityController must override this method and resolve the + * related GrantEntity for a given Entity. + * For example, the GrantEntity of Indicator is the related Exam + * + * @param entity the Entity to get the related GrantEntity for + * @return the GrantEntity instance for a given Entity instance */ + protected GrantEntity toGrantEntity(final T entity) { + if (entity == null) { + return null; + } + if (entity instanceof GrantEntity) { return (GrantEntity) entity; } @@ -411,8 +443,25 @@ public abstract class EntityController { + "Do override the toGrantEntity method from EntityController within the specific -Controller implementation"); } + /** Get the EntityType of the GrantEntity that is used for grant checks of the concrete Entity. + * + * NOTE: override this if the EntityType of the GrantEntity is not the same as the Entity itself. + * For example, the Exam is the GrantEntity of a Indicator + * + * @return the EntityType of the GrantEntity that is used for grant checks of the concrete Entity */ + protected EntityType getGrantEntityType() { + return this.entityDAO.entityType(); + } + + /** Implements the creation of a new entity from the post parameters given within the POSTMapper + * + * @param postParams contains all post parameter from request + * @return new created Entity instance */ protected abstract M createNew(POSTMapper postParams); + /** Gets the MyBatis SqlTable for the concrete Entity + * + * @return the MyBatis SqlTable for the concrete Entity */ protected abstract SqlTable getSQLTableOfEntity(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java index 62535887..f279796d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/IndicatorController.java @@ -13,13 +13,16 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; 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.IndicatorRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; 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.IndicatorDAO; @@ -67,4 +70,29 @@ public class IndicatorController extends EntityController return IndicatorRecordDynamicSqlSupport.indicatorRecord; } + @Override + protected Result checkCreateAccess(final Indicator entity) { + if (entity == null) { + return null; + } + + this.authorization.checkWrite(this.examDao.byPK(entity.examId).getOrThrow()); + return Result.of(entity); + } + + @Override + protected GrantEntity toGrantEntity(final Indicator entity) { + if (entity == null) { + return null; + } + + return this.examDao.byPK(entity.examId) + .getOrThrow(); + } + + @Override + protected EntityType getGrantEntityType() { + return EntityType.EXAM; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/OrientationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/OrientationController.java new file mode 100644 index 00000000..9294c609 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/OrientationController.java @@ -0,0 +1,84 @@ +/* + * 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.mybatis.dynamic.sql.SqlTable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation; +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.OrientationRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService; +import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.OrientationDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; + +@WebServiceProfile +@RestController +@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + API.ORIENTATION_ENDPOINT) +public class OrientationController extends EntityController { + + private final ConfigurationAttributeDAO configurationAttributeDAO; + + protected OrientationController( + final AuthorizationService authorization, + final BulkActionService bulkActionService, + final OrientationDAO entityDAO, + final ConfigurationAttributeDAO configurationAttributeDAO, + final UserActivityLogDAO userActivityLogDAO, + final PaginationService paginationService, + final BeanValidationService beanValidationService) { + + super(authorization, + bulkActionService, + entityDAO, + userActivityLogDAO, + paginationService, + beanValidationService); + + this.configurationAttributeDAO = configurationAttributeDAO; + } + + @Override + protected Orientation createNew(final POSTMapper postParams) { + final Long attributeId = postParams.getLong(Domain.ORIENTATION.ATTR_CONFIG_ATTRIBUTE_ID); + + return this.configurationAttributeDAO + .byPK(attributeId) + .map(attr -> new Orientation(attr, postParams)) + .getOrThrow(); + } + + @Override + protected SqlTable getSQLTableOfEntity() { + return OrientationRecordDynamicSqlSupport.orientationRecord; + } + + @Override + protected Result checkCreateAccess(final Orientation entity) { + // Skips the entity based grant check + return Result.of(entity); + } + + @Override + protected GrantEntity toGrantEntity(final Orientation entity) { + // Skips the entity based grant check + return null; + } + +} diff --git a/src/main/resources/schema-demo.sql b/src/main/resources/schema-demo.sql index 1498e791..c6f86ab2 100644 --- a/src/main/resources/schema-demo.sql +++ b/src/main/resources/schema-demo.sql @@ -180,10 +180,16 @@ CREATE TABLE IF NOT EXISTS `configuration` ( `version_date` DATETIME NULL, `followup` INT(1) NOT NULL, PRIMARY KEY (`id`), - INDEX `configurationNodeRef_idx` (`configuration_node_id` ASC, `institution_id` ASC), - CONSTRAINT `configurationNodeRef` - FOREIGN KEY (`configuration_node_id` , `institution_id`) - REFERENCES `configuration_node` (`id` , `institution_id`) + INDEX `configurationNodeRef_idx` (`configuration_node_id` ASC), + INDEX `config_institution_ref_idx` (`institution_id` ASC), + CONSTRAINT `configuration_node_ref` + FOREIGN KEY (`configuration_node_id`) + REFERENCES `configuration_node` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `config_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ; @@ -227,17 +233,23 @@ CREATE TABLE IF NOT EXISTS `configuration_value` ( `value` VARCHAR(255) NULL, `text` MEDIUMTEXT NULL, PRIMARY KEY (`id`), - INDEX `configuration_value_ref_idx` (`configuration_id` ASC, `institution_id` ASC), + INDEX `configuration_value_ref_idx` (`configuration_id` ASC), INDEX `configuration_attribute_ref_idx` (`configuration_attribute_id` ASC), + INDEX `configuration_value_institution_ref_idx` (`institution_id` ASC), CONSTRAINT `configuration_ref` - FOREIGN KEY (`configuration_id` , `institution_id`) - REFERENCES `configuration` (`id` , `institution_id`) + FOREIGN KEY (`configuration_id`) + REFERENCES `configuration` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `configuration_value_attribute_ref` FOREIGN KEY (`configuration_attribute_id`) REFERENCES `configuration_attribute` (`id`) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `configuration_value_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) + ON DELETE NO ACTION ON UPDATE NO ACTION) ; @@ -279,16 +291,22 @@ CREATE TABLE IF NOT EXISTS `exam_configuration_map` ( `configuration_node_id` BIGINT UNSIGNED NOT NULL, `user_names` VARCHAR(4000) NULL, PRIMARY KEY (`id`), - INDEX `exam_ref_idx` (`exam_id` ASC, `institution_id` ASC), - INDEX `configuration_map_ref_idx` (`configuration_node_id` ASC, `institution_id` ASC), + INDEX `exam_ref_idx` (`exam_id` ASC), + INDEX `configuration_map_ref_idx` (`configuration_node_id` ASC), + INDEX `exam_config_institution_ref_idx` (`institution_id` ASC), CONSTRAINT `exam_map_ref` - FOREIGN KEY (`exam_id` , `institution_id`) - REFERENCES `exam` (`id` , `institution_id`) + FOREIGN KEY (`exam_id`) + REFERENCES `exam` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `configuration_map_ref` - FOREIGN KEY (`configuration_node_id` , `institution_id`) - REFERENCES `configuration_node` (`id` , `institution_id`) + FOREIGN KEY (`configuration_node_id`) + REFERENCES `configuration_node` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `exam_config_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ; diff --git a/src/main/resources/schema-dev.sql b/src/main/resources/schema-dev.sql index d4b0ace9..582bd538 100644 --- a/src/main/resources/schema-dev.sql +++ b/src/main/resources/schema-dev.sql @@ -185,10 +185,16 @@ CREATE TABLE IF NOT EXISTS `configuration` ( `version_date` DATETIME NULL, `followup` INT(1) NOT NULL, PRIMARY KEY (`id`), - INDEX `configurationNodeRef_idx` (`configuration_node_id` ASC, `institution_id` ASC), - CONSTRAINT `configurationNodeRef` - FOREIGN KEY (`configuration_node_id` , `institution_id`) - REFERENCES `configuration_node` (`id` , `institution_id`) + INDEX `configurationNodeRef_idx` (`configuration_node_id` ASC), + INDEX `config_institution_ref_idx` (`institution_id` ASC), + CONSTRAINT `configuration_node_ref` + FOREIGN KEY (`configuration_node_id`) + REFERENCES `configuration_node` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `config_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ; @@ -232,17 +238,23 @@ CREATE TABLE IF NOT EXISTS `configuration_value` ( `value` VARCHAR(255) NULL, `text` MEDIUMTEXT NULL, PRIMARY KEY (`id`), - INDEX `configuration_value_ref_idx` (`configuration_id` ASC, `institution_id` ASC), + INDEX `configuration_value_ref_idx` (`configuration_id` ASC), INDEX `configuration_attribute_ref_idx` (`configuration_attribute_id` ASC), + INDEX `configuration_value_institution_ref_idx` (`institution_id` ASC), CONSTRAINT `configuration_ref` - FOREIGN KEY (`configuration_id` , `institution_id`) - REFERENCES `configuration` (`id` , `institution_id`) + FOREIGN KEY (`configuration_id`) + REFERENCES `configuration` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `configuration_value_attribute_ref` FOREIGN KEY (`configuration_attribute_id`) REFERENCES `configuration_attribute` (`id`) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `configuration_value_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) + ON DELETE NO ACTION ON UPDATE NO ACTION) ; @@ -284,16 +296,22 @@ CREATE TABLE IF NOT EXISTS `exam_configuration_map` ( `configuration_node_id` BIGINT UNSIGNED NOT NULL, `user_names` VARCHAR(4000) NULL, PRIMARY KEY (`id`), - INDEX `exam_ref_idx` (`exam_id` ASC, `institution_id` ASC), - INDEX `configuration_map_ref_idx` (`configuration_node_id` ASC, `institution_id` ASC), + INDEX `exam_ref_idx` (`exam_id` ASC), + INDEX `configuration_map_ref_idx` (`configuration_node_id` ASC), + INDEX `exam_config_institution_ref_idx` (`institution_id` ASC), CONSTRAINT `exam_map_ref` - FOREIGN KEY (`exam_id` , `institution_id`) - REFERENCES `exam` (`id` , `institution_id`) + FOREIGN KEY (`exam_id`) + REFERENCES `exam` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `configuration_map_ref` - FOREIGN KEY (`configuration_node_id` , `institution_id`) - REFERENCES `configuration_node` (`id` , `institution_id`) + FOREIGN KEY (`configuration_node_id`) + REFERENCES `configuration_node` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `exam_config_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ; diff --git a/src/test/resources/schema-test.sql b/src/test/resources/schema-test.sql index 1498e791..c6f86ab2 100644 --- a/src/test/resources/schema-test.sql +++ b/src/test/resources/schema-test.sql @@ -180,10 +180,16 @@ CREATE TABLE IF NOT EXISTS `configuration` ( `version_date` DATETIME NULL, `followup` INT(1) NOT NULL, PRIMARY KEY (`id`), - INDEX `configurationNodeRef_idx` (`configuration_node_id` ASC, `institution_id` ASC), - CONSTRAINT `configurationNodeRef` - FOREIGN KEY (`configuration_node_id` , `institution_id`) - REFERENCES `configuration_node` (`id` , `institution_id`) + INDEX `configurationNodeRef_idx` (`configuration_node_id` ASC), + INDEX `config_institution_ref_idx` (`institution_id` ASC), + CONSTRAINT `configuration_node_ref` + FOREIGN KEY (`configuration_node_id`) + REFERENCES `configuration_node` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `config_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ; @@ -227,17 +233,23 @@ CREATE TABLE IF NOT EXISTS `configuration_value` ( `value` VARCHAR(255) NULL, `text` MEDIUMTEXT NULL, PRIMARY KEY (`id`), - INDEX `configuration_value_ref_idx` (`configuration_id` ASC, `institution_id` ASC), + INDEX `configuration_value_ref_idx` (`configuration_id` ASC), INDEX `configuration_attribute_ref_idx` (`configuration_attribute_id` ASC), + INDEX `configuration_value_institution_ref_idx` (`institution_id` ASC), CONSTRAINT `configuration_ref` - FOREIGN KEY (`configuration_id` , `institution_id`) - REFERENCES `configuration` (`id` , `institution_id`) + FOREIGN KEY (`configuration_id`) + REFERENCES `configuration` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `configuration_value_attribute_ref` FOREIGN KEY (`configuration_attribute_id`) REFERENCES `configuration_attribute` (`id`) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `configuration_value_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) + ON DELETE NO ACTION ON UPDATE NO ACTION) ; @@ -279,16 +291,22 @@ CREATE TABLE IF NOT EXISTS `exam_configuration_map` ( `configuration_node_id` BIGINT UNSIGNED NOT NULL, `user_names` VARCHAR(4000) NULL, PRIMARY KEY (`id`), - INDEX `exam_ref_idx` (`exam_id` ASC, `institution_id` ASC), - INDEX `configuration_map_ref_idx` (`configuration_node_id` ASC, `institution_id` ASC), + INDEX `exam_ref_idx` (`exam_id` ASC), + INDEX `configuration_map_ref_idx` (`configuration_node_id` ASC), + INDEX `exam_config_institution_ref_idx` (`institution_id` ASC), CONSTRAINT `exam_map_ref` - FOREIGN KEY (`exam_id` , `institution_id`) - REFERENCES `exam` (`id` , `institution_id`) + FOREIGN KEY (`exam_id`) + REFERENCES `exam` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT `configuration_map_ref` - FOREIGN KEY (`configuration_node_id` , `institution_id`) - REFERENCES `configuration_node` (`id` , `institution_id`) + FOREIGN KEY (`configuration_node_id`) + REFERENCES `configuration_node` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `exam_config_institution_ref` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION) ;