SEBSERV-15 #added bulk load and report to BulkActionService

This commit is contained in:
anhefti 2019-01-14 15:20:03 +01:00
parent 2ecf709d8d
commit bcc423bd2e
20 changed files with 445 additions and 64 deletions

View file

@ -12,4 +12,6 @@ public interface Entity extends ModelIdAware {
EntityType entityType(); EntityType entityType();
String getName();
} }

View file

@ -9,30 +9,53 @@
package ch.ethz.seb.sebserver.gbl.model; package ch.ethz.seb.sebserver.gbl.model;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class EntityIdAndName implements ModelIdAware, ModelNameAware { public class EntityKeyAndName implements ModelIdAware, ModelNameAware {
@JsonProperty(value = "entityType", required = true)
public final EntityType entityType;
@JsonProperty(value = Domain.ATTR_ID, required = true)
public final String id; public final String id;
@JsonProperty(value = "name", required = true)
public final String name; public final String name;
@JsonCreator @JsonCreator
public EntityIdAndName( public EntityKeyAndName(
@JsonProperty(value = "entityType", required = true) final EntityType entityType,
@JsonProperty(value = Domain.ATTR_ID, required = true) final String id, @JsonProperty(value = Domain.ATTR_ID, required = true) final String id,
@JsonProperty(value = "name", required = true) final String name) { @JsonProperty(value = "name", required = true) final String name) {
this.entityType = entityType;
this.id = id; this.id = id;
this.name = name; this.name = name;
} }
public EntityKeyAndName(final EntityKey entityKey, final String name) {
this.entityType = entityKey.entityType;
this.id = entityKey.entityId;
this.name = name;
}
public EntityType getEntityType() {
return this.entityType;
}
public String getId() {
return this.id;
}
@Override @Override
public String getName() { public String getName() {
return this.name; return this.name;
} }
@Override @Override
@JsonIgnore
public String getModelId() { public String getModelId() {
return this.id; return this.id;
} }
@ -41,6 +64,7 @@ public class EntityIdAndName implements ModelIdAware, ModelNameAware {
public int hashCode() { public int hashCode() {
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.id == null) ? 0 : this.id.hashCode()); result = prime * result + ((this.id == null) ? 0 : this.id.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;
@ -54,7 +78,9 @@ public class EntityIdAndName implements ModelIdAware, ModelNameAware {
return false; return false;
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
final EntityIdAndName other = (EntityIdAndName) obj; final EntityKeyAndName other = (EntityKeyAndName) obj;
if (this.entityType != other.entityType)
return false;
if (this.id == null) { if (this.id == null) {
if (other.id != null) if (other.id != null)
return false; return false;
@ -70,7 +96,7 @@ public class EntityIdAndName implements ModelIdAware, ModelNameAware {
@Override @Override
public String toString() { public String toString() {
return "IdAndName [id=" + this.id + ", name=" + this.name + "]"; return "EntityIdAndName [entityType=" + this.entityType + ", id=" + this.id + ", name=" + this.name + "]";
} }
} }

View file

@ -8,13 +8,31 @@
package ch.ethz.seb.sebserver.gbl.model; package ch.ethz.seb.sebserver.gbl.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class EntityProcessingReport { public class EntityProcessingReport {
// TODO @JsonProperty(value = "source", required = true)
public final Collection<Entity> source;
@JsonProperty(value = "dependencies", required = true)
public final Collection<EntityKeyAndName> dependencies;
@JsonProperty(value = "errors", required = true)
public final Map<EntityKeyAndName, String> errors;
public EntityProcessingReport add(final Entity entity) { @JsonCreator
// TODO public EntityProcessingReport(
return this; @JsonProperty(value = "source", required = true) final Collection<Entity> source,
@JsonProperty(value = "dependencies", required = true) final Collection<EntityKeyAndName> dependencies,
@JsonProperty(value = "errors", required = true) final Map<EntityKeyAndName, String> errors) {
this.source = Collections.unmodifiableCollection(source);
this.dependencies = Collections.unmodifiableCollection(dependencies);
this.errors = Collections.unmodifiableMap(errors);
} }
} }

View file

@ -17,7 +17,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.Activatable; import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION;
import ch.ethz.seb.sebserver.gbl.model.EntityIdAndName; import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
@ -86,6 +86,7 @@ public final class Institution implements GrantEntity, Activatable {
return null; return null;
} }
@Override
public String getName() { public String getName() {
return this.name; return this.name;
} }
@ -109,8 +110,11 @@ public final class Institution implements GrantEntity, Activatable {
+ ", active=" + this.active + "]"; + ", active=" + this.active + "]";
} }
public static EntityIdAndName toName(final Institution institution) { public static EntityKeyAndName toName(final Institution institution) {
return new EntityIdAndName(String.valueOf(institution.id), institution.name); return new EntityKeyAndName(
EntityType.INSTITUTION,
String.valueOf(institution.id),
institution.name);
} }
} }

View file

@ -19,11 +19,17 @@ import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION; import ch.ethz.seb.sebserver.gbl.model.Domain.INSTITUTION;
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP; import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
import ch.ethz.seb.sebserver.gbl.model.EntityKeyAndName;
import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.GrantEntity;
public final class LmsSetup implements GrantEntity, Activatable { public final class LmsSetup implements GrantEntity, Activatable {
public static final String FILTER_ATTR_INSTITUTION = "institution";
public static final String FILTER_ATTR_NAME = "name";
public static final String FILTER_ATTR_LMS_TYPE = "lms_type";
public static final String FILTER_ATTR_ACTIVE = "active";
public enum LMSType { public enum LMSType {
MOCKUP, MOCKUP,
MOODLE, MOODLE,
@ -133,6 +139,7 @@ public final class LmsSetup implements GrantEntity, Activatable {
return this.institutionId; return this.institutionId;
} }
@Override
public String getName() { public String getName() {
return this.name; return this.name;
} }
@ -179,4 +186,11 @@ public final class LmsSetup implements GrantEntity, Activatable {
+ this.sebAuthSecret + ", active=" + this.active + "]"; + this.sebAuthSecret + ", active=" + this.active + "]";
} }
public static EntityKeyAndName toName(final LmsSetup lmsSetup) {
return new EntityKeyAndName(
EntityType.LMS_SETUP,
String.valueOf(lmsSetup.id),
lmsSetup.name);
}
} }

View file

@ -84,6 +84,12 @@ public class UserActivityLog implements Entity {
: null; : null;
} }
@Override
@JsonIgnore
public String getName() {
return getModelId();
}
public String getUserUuid() { public String getUserUuid() {
return this.userUUID; return this.userUUID;
} }

View file

@ -15,7 +15,7 @@ public final class UserFilter {
public static final String FILTER_ATTR_USER_NAME = "username"; public static final String FILTER_ATTR_USER_NAME = "username";
public static final String FILTER_ATTR_EMAIL = "email"; public static final String FILTER_ATTR_EMAIL = "email";
public static final String FILTER_ATTR_LOCALE = "locale"; public static final String FILTER_ATTR_LOCALE = "locale";
public static final String FILTER_ATTR_INSTITUTION = "institutionId"; public static final String FILTER_ATTR_INSTITUTION = "institution";
public final Boolean active; public final Boolean active;
public final Long institutionId; public final Long institutionId;

View file

@ -122,6 +122,7 @@ public final class UserInfo implements GrantEntity, Activatable, Serializable {
return this.uuid; return this.uuid;
} }
@Override
public String getName() { public String getName() {
return this.name; return this.name;
} }

View file

@ -149,6 +149,7 @@ public final class UserMod implements GrantEntity {
return this.newPassword; return this.newPassword;
} }
@Override
public String getName() { public String getName() {
return this.name; return this.name;
} }

View file

@ -27,4 +27,9 @@ public enum UserRole implements Entity {
public String getModelId() { public String getModelId() {
return name(); return name();
} }
@Override
public String getName() {
return name();
}
} }

View file

@ -21,6 +21,11 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
* has write, modify or even read-only rights on an entity instance or on an entity type. */ * has write, modify or even read-only rights on an entity instance or on an entity type. */
public interface AuthorizationGrantService { public interface AuthorizationGrantService {
/** Gets the UserService that is bundled within the AuthorizationGrantService
*
* @return the UserService that is bundled within the AuthorizationGrantService */
UserService getUserService();
/** Checks if the current user has any privilege (base or institutional or owner) for the given EntityType and /** Checks if the current user has any privilege (base or institutional or owner) for the given EntityType and
* PrivilegeType. * PrivilegeType.
* *

View file

@ -51,6 +51,11 @@ public class AuthorizationGrantServiceImpl implements AuthorizationGrantService
} }
} }
@Override
public UserService getUserService() {
return this.userService;
}
/** Initialize the (hard-coded) grants */ /** Initialize the (hard-coded) grants */
@PostConstruct @PostConstruct
public void init() { public void init() {

View file

@ -8,13 +8,18 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction; package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
@ -22,20 +27,23 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
@WebServiceProfile @WebServiceProfile
public class BulkActionService { public class BulkActionService {
private final Collection<BulkActionSupport> supporter; private final Map<EntityType, BulkActionSupport> supporter;
private final UserActivityLogDAO userActivityLogDAO; private final UserActivityLogDAO userActivityLogDAO;
public BulkActionService( public BulkActionService(
final Collection<BulkActionSupport> supporter, final Collection<BulkActionSupport> supporter,
final UserActivityLogDAO userActivityLogDAO) { final UserActivityLogDAO userActivityLogDAO) {
this.supporter = supporter; this.supporter = new HashMap<>();
for (final BulkActionSupport support : supporter) {
this.supporter.put(support.entityType(), support);
}
this.userActivityLogDAO = userActivityLogDAO; this.userActivityLogDAO = userActivityLogDAO;
} }
public void collectDependencies(final BulkAction action) { public void collectDependencies(final BulkAction action) {
checkProcessing(action); checkProcessing(action);
for (final BulkActionSupport sup : this.supporter) { for (final BulkActionSupport sup : this.supporter.values()) {
action.dependencies.addAll(sup.getDependencies(action)); action.dependencies.addAll(sup.getDependencies(action));
} }
action.alreadyProcessed = true; action.alreadyProcessed = true;
@ -44,7 +52,7 @@ public class BulkActionService {
public void doBulkAction(final BulkAction action) { public void doBulkAction(final BulkAction action) {
checkProcessing(action); checkProcessing(action);
final BulkActionSupport supportForSource = getSupporterForSource(action); final BulkActionSupport supportForSource = this.supporter.get(action.sourceType);
if (supportForSource == null) { if (supportForSource == null) {
action.alreadyProcessed = true; action.alreadyProcessed = true;
return; return;
@ -54,10 +62,10 @@ public class BulkActionService {
if (!action.dependencies.isEmpty()) { if (!action.dependencies.isEmpty()) {
// process dependencies first... // process dependencies first...
final List<BulkActionSupport> dependantSupporterInHierarchicalOrder = final List<BulkActionSupport> dependancySupporter =
getDependantSupporterInHierarchicalOrder(action); getDependancySupporter(action);
for (final BulkActionSupport support : dependantSupporterInHierarchicalOrder) { for (final BulkActionSupport support : dependancySupporter) {
action.result.addAll(support.processBulkAction(action)); action.result.addAll(support.processBulkAction(action));
} }
} }
@ -73,14 +81,19 @@ public class BulkActionService {
doBulkAction(action); doBulkAction(action);
} }
final EntityProcessingReport report = new EntityProcessingReport();
// TODO // TODO
return report; return new EntityProcessingReport(
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyMap());
} }
private void processUserActivityLog(final BulkAction action) { private void processUserActivityLog(final BulkAction action) {
if (action.type.activityType == null) {
return;
}
for (final EntityKey key : action.dependencies) { for (final EntityKey key : action.dependencies) {
this.userActivityLogDAO.log( this.userActivityLogDAO.log(
action.type.activityType, action.type.activityType,
@ -98,21 +111,50 @@ public class BulkActionService {
} }
} }
private BulkActionSupport getSupporterForSource(final BulkAction action) { private List<BulkActionSupport> getDependancySupporter(final BulkAction action) {
for (final BulkActionSupport support : this.supporter) { switch (action.type) {
if (support.entityType() == action.sourceType) { case ACTIVATE:
return support; case DEACTIVATE:
case HARD_DELETE: {
final List<BulkActionSupport> dependantSupporterInHierarchicalOrder =
getDependantSupporterInHierarchicalOrder(action);
Collections.reverse(dependantSupporterInHierarchicalOrder);
return dependantSupporterInHierarchicalOrder;
} }
default:
return getDependantSupporterInHierarchicalOrder(action);
} }
return null;
} }
private List<BulkActionSupport> getDependantSupporterInHierarchicalOrder(final BulkAction action) { private List<BulkActionSupport> getDependantSupporterInHierarchicalOrder(final BulkAction action) {
switch (action.sourceType) {
// TODO case INSTITUTION:
return Arrays.asList(
return null; this.supporter.get(EntityType.LMS_SETUP),
this.supporter.get(EntityType.USER),
this.supporter.get(EntityType.EXAM),
this.supporter.get(EntityType.CLIENT_CONNECTION),
this.supporter.get(EntityType.CONFIGURATION_NODE));
case LMS_SETUP:
return Arrays.asList(
this.supporter.get(EntityType.EXAM),
this.supporter.get(EntityType.CLIENT_CONNECTION));
case USER:
return Arrays.asList(
this.supporter.get(EntityType.EXAM),
this.supporter.get(EntityType.CLIENT_CONNECTION),
this.supporter.get(EntityType.CONFIGURATION_NODE));
case EXAM:
return Arrays.asList(
this.supporter.get(EntityType.EXAM),
this.supporter.get(EntityType.CLIENT_CONNECTION));
case CONFIGURATION:
return Arrays.asList(
this.supporter.get(EntityType.EXAM),
this.supporter.get(EntityType.CLIENT_CONNECTION));
default:
return Collections.emptyList();
}
} }
private void checkProcessing(final BulkAction action) { private void checkProcessing(final BulkAction action) {

View file

@ -10,8 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.model.Entity;
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.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
@ -24,6 +29,22 @@ public interface BulkActionSupport {
Set<EntityKey> getDependencies(BulkAction bulkAction); Set<EntityKey> getDependencies(BulkAction bulkAction);
Result<Collection<Entity>> bulkLoadEntities(Collection<EntityKey> keys);
@Transactional(readOnly = true)
default Result<Collection<EntityKeyAndName>> bulkLoadEntityNames(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> {
return bulkLoadEntities(keys)
.getOrThrow()
.stream()
.map(entity -> new EntityKeyAndName(
EntityType.INSTITUTION,
entity.getModelId(),
entity.getName()))
.collect(Collectors.toList());
});
}
Collection<Result<EntityKey>> processBulkAction(BulkAction bulkAction); Collection<Result<EntityKey>> processBulkAction(BulkAction bulkAction);
} }

View file

@ -27,6 +27,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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;
@ -150,7 +151,6 @@ public class InstitutionDAOImpl implements InstitutionDAO, BulkActionSupport {
@Transactional @Transactional
public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) { public Collection<Result<EntityKey>> delete(final Set<EntityKey> all) {
final Collection<Result<EntityKey>> result = new ArrayList<>(); final Collection<Result<EntityKey>> result = new ArrayList<>();
final List<Long> ids = extractIdsFromKeys(all, result); final List<Long> ids = extractIdsFromKeys(all, result);
try { try {
@ -177,6 +177,24 @@ public class InstitutionDAOImpl implements InstitutionDAO, BulkActionSupport {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override
@Transactional(readOnly = true)
public Result<Collection<Entity>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> {
final Collection<Result<EntityKey>> result = new ArrayList<>();
final List<Long> ids = extractIdsFromKeys(keys, result);
return this.institutionRecordMapper.selectByExample()
.where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute()
.stream()
.map(InstitutionDAOImpl::toDomainModel)
.map(res -> res.getOrThrow())
.collect(Collectors.toList());
});
}
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) { public Collection<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) {

View file

@ -29,11 +29,13 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LMSType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LMSType;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.LmsSetupRecord; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.LmsSetupRecord;
@ -210,6 +212,24 @@ public class LmsSetupDAOImpl implements LmsSetupDAO, BulkActionSupport {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override
@Transactional(readOnly = true)
public Result<Collection<Entity>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> {
final Collection<Result<EntityKey>> result = new ArrayList<>();
final List<Long> ids = extractIdsFromKeys(keys, result);
return this.lmsSetupRecordMapper.selectByExample()
.where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute()
.stream()
.map(LmsSetupDAOImpl::toDomainModel)
.map(res -> res.getOrThrow())
.collect(Collectors.toList());
});
}
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) { public Collection<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) {

View file

@ -39,12 +39,14 @@ import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.model.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.model.APIMessage.ErrorMessage; import ch.ethz.seb.sebserver.gbl.model.APIMessage.ErrorMessage;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.user.UserFilter; import ch.ethz.seb.sebserver.gbl.model.user.UserFilter;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserMod; import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RoleRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RoleRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RoleRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RoleRecordMapper;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
@ -274,6 +276,24 @@ public class UserDaoImpl implements UserDAO, BulkActionSupport {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override
@Transactional(readOnly = true)
public Result<Collection<Entity>> bulkLoadEntities(final Collection<EntityKey> keys) {
return Result.tryCatch(() -> {
final Collection<Result<EntityKey>> result = new ArrayList<>();
final List<Long> ids = extractIdsFromKeys(keys, result);
return this.userRecordMapper.selectByExample()
.where(InstitutionRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute()
.stream()
.map(this::toDomainModel)
.map(res -> res.getOrThrow())
.collect(Collectors.toList());
});
}
@Override @Override
@Transactional @Transactional
public Collection<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) { public Collection<Result<EntityKey>> processBulkAction(final BulkAction bulkAction) {

View file

@ -21,7 +21,7 @@ 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.EntityIdAndName; 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.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;
@ -31,7 +31,6 @@ 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.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.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;
@ -46,19 +45,17 @@ public class InstitutionController {
private final InstitutionDAO institutionDAO; private final InstitutionDAO institutionDAO;
private final AuthorizationGrantService authorizationGrantService; private final AuthorizationGrantService authorizationGrantService;
private final UserService userService;
private final UserActivityLogDAO userActivityLogDAO; private final UserActivityLogDAO userActivityLogDAO;
private final BulkActionService bulkActionService; private final BulkActionService bulkActionService;
public InstitutionController( public InstitutionController(
final InstitutionDAO institutionDAO, final InstitutionDAO institutionDAO,
final AuthorizationGrantService authorizationGrantService, final AuthorizationGrantService authorizationGrantService,
final UserService userService, final UserActivityLogDAO userActivityLogDAO, final UserActivityLogDAO userActivityLogDAO,
final BulkActionService bulkActionService) { final BulkActionService bulkActionService) {
this.institutionDAO = institutionDAO; this.institutionDAO = institutionDAO;
this.authorizationGrantService = authorizationGrantService; this.authorizationGrantService = authorizationGrantService;
this.userService = userService;
this.userActivityLogDAO = userActivityLogDAO; this.userActivityLogDAO = userActivityLogDAO;
this.bulkActionService = bulkActionService; this.bulkActionService = bulkActionService;
} }
@ -68,7 +65,9 @@ public class InstitutionController {
checkBaseReadPrivilege(); checkBaseReadPrivilege();
final SEBServerUser currentUser = this.userService.getCurrentUser(); final SEBServerUser currentUser = this.authorizationGrantService
.getUserService()
.getCurrentUser();
final Long institutionId = currentUser.institutionId(); final Long institutionId = currentUser.institutionId();
return this.institutionDAO.byId(institutionId).getOrThrow(); return this.institutionDAO.byId(institutionId).getOrThrow();
@ -94,7 +93,7 @@ public class InstitutionController {
checkBaseReadPrivilege(); checkBaseReadPrivilege();
if (!this.authorizationGrantService.hasBasePrivilege( if (!this.authorizationGrantService.hasBasePrivilege(
EntityType.USER, EntityType.INSTITUTION,
PrivilegeType.READ_ONLY)) { PrivilegeType.READ_ONLY)) {
// User has only institutional privilege, can see only the institution he/she belongs to // User has only institutional privilege, can see only the institution he/she belongs to
@ -105,40 +104,24 @@ public class InstitutionController {
} }
@RequestMapping(path = "/names", method = RequestMethod.GET) @RequestMapping(path = "/names", method = RequestMethod.GET)
public Collection<EntityIdAndName> getNames( public Collection<EntityKeyAndName> getNames(
@RequestParam(name = Institution.FILTER_ATTR_ACTIVE, required = false) final Boolean active) { @RequestParam(name = Institution.FILTER_ATTR_ACTIVE, required = false) final Boolean active) {
checkBaseReadPrivilege(); return getAll(active)
.stream()
if (!this.authorizationGrantService.hasBasePrivilege( .map(Institution::toName)
EntityType.USER, .collect(Collectors.toList());
PrivilegeType.READ_ONLY)) {
// User has only institutional privilege, can see only the institution he/she belongs to
return Arrays.asList(getOwn())
.stream()
.map(Institution::toName)
.collect(Collectors.toList());
} else {
return this.institutionDAO.all(inst -> true, active)
.getOrThrow()
.stream()
.map(Institution::toName)
.collect(Collectors.toList());
}
} }
@RequestMapping(path = "/create", method = RequestMethod.PUT) @RequestMapping(path = "/create", method = RequestMethod.PUT)
public Institution create(@Valid @RequestBody final Institution institution) { public Institution create(@Valid @RequestBody final Institution institution) {
return _saveInstitution(institution, PrivilegeType.WRITE) return save(institution, PrivilegeType.WRITE)
.getOrThrow(); .getOrThrow();
} }
@RequestMapping(path = "/save", method = RequestMethod.POST) @RequestMapping(path = "/save", method = RequestMethod.POST)
public Institution save(@Valid @RequestBody final Institution institution) { public Institution save(@Valid @RequestBody final Institution institution) {
return _saveInstitution(institution, PrivilegeType.MODIFY) return save(institution, PrivilegeType.MODIFY)
.getOrThrow(); .getOrThrow();
} }
@ -197,7 +180,7 @@ public class InstitutionController {
.getOrThrow(); .getOrThrow();
} }
private Result<Institution> _saveInstitution(final Institution institution, final PrivilegeType privilegeType) { private Result<Institution> save(final Institution institution, final PrivilegeType privilegeType) {
final ActivityType activityType = (institution.id == null) final ActivityType activityType = (institution.id == null)
? ActivityType.CREATE ? ActivityType.CREATE

View file

@ -8,14 +8,199 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api; package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Collection;
import java.util.stream.Collectors;
import javax.validation.Valid;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
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.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.EntityType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
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.webservice.servicelayer.authorization.AuthorizationGrantService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.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.LmsSetupDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO.ActivityType;
@WebServiceProfile @WebServiceProfile
@RestController @RestController
@RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_LMS_SETUP) @RequestMapping("/${sebserver.webservice.api.admin.endpoint}" + RestAPI.ENDPOINT_LMS_SETUP)
public class LmsSetupController { public class LmsSetupController {
private final LmsSetupDAO lmsSetupDAO;
private final AuthorizationGrantService authorizationGrantService;
private final UserActivityLogDAO userActivityLogDAO;
private final BulkActionService bulkActionService;
public LmsSetupController(
final LmsSetupDAO lmsSetupDAO,
final AuthorizationGrantService authorizationGrantService,
final UserActivityLogDAO userActivityLogDAO,
final BulkActionService bulkActionService) {
this.lmsSetupDAO = lmsSetupDAO;
this.authorizationGrantService = authorizationGrantService;
this.userActivityLogDAO = userActivityLogDAO;
this.bulkActionService = bulkActionService;
}
@RequestMapping(method = RequestMethod.GET)
public Collection<LmsSetup> getAll(
@RequestParam(name = LmsSetup.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId,
@RequestParam(name = LmsSetup.FILTER_ATTR_NAME, required = false) final String name,
@RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LMSType lmsType,
@RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) {
checkBaseReadPrivilege();
final SEBServerUser currentUser = this.authorizationGrantService
.getUserService()
.getCurrentUser();
final Long usersInstitution = currentUser.institutionId();
final Long instId = (institutionId != null) ? institutionId : usersInstitution;
if (!this.authorizationGrantService.hasBasePrivilege(
EntityType.LMS_SETUP,
PrivilegeType.READ_ONLY) &&
instId != usersInstitution) {
throw new PermissionDeniedException(
EntityType.LMS_SETUP,
PrivilegeType.READ_ONLY,
currentUser.getUserInfo().uuid);
}
return this.lmsSetupDAO
.allMatching(instId, name, lmsType, active)
.getOrThrow();
}
@RequestMapping(path = "/names", method = RequestMethod.GET)
public Collection<EntityKeyAndName> getNames(
@RequestParam(name = LmsSetup.FILTER_ATTR_INSTITUTION, required = false) final Long institutionId,
@RequestParam(name = LmsSetup.FILTER_ATTR_NAME, required = false) final String name,
@RequestParam(name = LmsSetup.FILTER_ATTR_LMS_TYPE, required = false) final LMSType lmsType,
@RequestParam(name = LmsSetup.FILTER_ATTR_ACTIVE, required = false) final Boolean active) {
return getAll(institutionId, name, lmsType, active)
.stream()
.map(LmsSetup::toName)
.collect(Collectors.toList());
}
@RequestMapping(path = "/{id}", method = RequestMethod.GET)
public LmsSetup getById(@PathVariable final Long id) {
checkBaseReadPrivilege();
return this.lmsSetupDAO
.byId(id)
.flatMap(inst -> this.authorizationGrantService.checkGrantOnEntity(
inst,
PrivilegeType.READ_ONLY))
.getOrThrow();
}
@RequestMapping(path = "/create", method = RequestMethod.PUT)
public LmsSetup create(@Valid @RequestBody final LmsSetup lmsSetup) {
return save(lmsSetup, PrivilegeType.WRITE)
.getOrThrow();
}
@RequestMapping(path = "/save", method = RequestMethod.POST)
public LmsSetup save(@Valid @RequestBody final LmsSetup lmsSetup) {
return save(lmsSetup, PrivilegeType.MODIFY)
.getOrThrow();
}
@RequestMapping(path = "/{id}/activate", method = RequestMethod.POST)
public LmsSetup activate(@PathVariable final Long id) {
return setActive(id, true);
}
@RequestMapping(value = "/{id}/deactivate", method = RequestMethod.POST)
public LmsSetup deactivate(@PathVariable final Long id) {
return setActive(id, false);
}
@RequestMapping(path = "/{id}/delete", method = RequestMethod.DELETE)
public EntityProcessingReport deleteUser(@PathVariable final Long id) {
checkPrivilegeForInstitution(id, PrivilegeType.WRITE);
return this.bulkActionService.createReport(new BulkAction(
Type.DEACTIVATE,
EntityType.LMS_SETUP,
new EntityKey(id, EntityType.LMS_SETUP)));
}
@RequestMapping(path = "/{id}/hard-delete", method = RequestMethod.DELETE)
public EntityProcessingReport hardDeleteUser(@PathVariable final Long id) {
checkPrivilegeForInstitution(id, PrivilegeType.WRITE);
return this.bulkActionService.createReport(new BulkAction(
Type.HARD_DELETE,
EntityType.LMS_SETUP,
new EntityKey(id, EntityType.LMS_SETUP)));
}
private void checkPrivilegeForInstitution(final Long id, final PrivilegeType type) {
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.LMS_SETUP,
type);
this.lmsSetupDAO.byId(id)
.flatMap(institution -> this.authorizationGrantService.checkGrantOnEntity(
institution,
type))
.getOrThrow();
}
private LmsSetup setActive(final Long id, final boolean active) {
checkPrivilegeForInstitution(id, PrivilegeType.MODIFY);
this.bulkActionService.doBulkAction(new BulkAction(
(active) ? Type.ACTIVATE : Type.DEACTIVATE,
EntityType.LMS_SETUP,
new EntityKey(id, EntityType.LMS_SETUP)));
return this.lmsSetupDAO
.byId(id)
.getOrThrow();
}
private Result<LmsSetup> save(final LmsSetup lmsSetup, final PrivilegeType privilegeType) {
final ActivityType activityType = (lmsSetup.id == null)
? ActivityType.CREATE
: ActivityType.MODIFY;
return this.authorizationGrantService
.checkGrantOnEntity(lmsSetup, privilegeType)
.flatMap(this.lmsSetupDAO::save)
.flatMap(inst -> this.userActivityLogDAO.log(activityType, inst));
}
private void checkBaseReadPrivilege() {
this.authorizationGrantService.checkHasAnyPrivilege(
EntityType.LMS_SETUP,
PrivilegeType.READ_ONLY);
}
} }

View file

@ -100,6 +100,11 @@ public class AuthorizationGrantServiceTest {
return "1"; return "1";
} }
@Override
public String getName() {
return getModelId();
}
}; };
} }