Skip to content

Commit

Permalink
SEBSERV-587
Browse files Browse the repository at this point in the history
  • Loading branch information
anhefti committed Oct 2, 2024
1 parent 5996604 commit c730bc9
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@

package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring;

import javax.validation.constraints.Size;
import java.util.*;
import java.util.stream.Collectors;

import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ScreenProctoringGroupDAO;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -71,7 +74,7 @@ class ScreenProctoringAPIBinding {
private static final Logger log = LoggerFactory.getLogger(ScreenProctoringAPIBinding.class);

private static final String SEB_SERVER_SCREEN_PROCTORING_SEB_ACCESS_PREFIX = "SEBServer_SEB_Access_";

interface SPS_API {

enum SPSUserRole {
Expand All @@ -86,7 +89,6 @@ enum SPSUserRole {

String USER_ACCOUNT_ENDPOINT = "/admin-api/v1/useraccount/";
String USERSYNC_SEBSERVER_ENDPOINT = USER_ACCOUNT_ENDPOINT + "usersync/sebserver";
// String ENTITY_PRIVILEGES_ENDPOINT = USER_ACCOUNT_ENDPOINT + "entityprivilege";
String EXAM_ENDPOINT = "/admin-api/v1/exam";
String EXAM_DELETE_REQUEST_ENDPOINT = "/request";
String SEB_ACCESS_ENDPOINT = "/admin-api/v1/clientaccess";
Expand All @@ -95,21 +97,6 @@ enum SPSUserRole {
String ACTIVE_PATH_SEGMENT = "/active";
String INACTIVE_PATH_SEGMENT = "/inactive";


// interface ENTITY_PRIVILEGE {
// String ATTR_ENTITY_TYPE = "entityType";
// String ATTR_ENTITY_ID = "entityId";
// String ATTR_USER_UUID = "userUuid";
// String ATTR_USERNAME = "username";
// String ATTR_PRIVILEGES = "privileges";
// }
//
// interface PRIVILEGE_FLAGS {
// String READ = "r";
// String MODIFY = "m";
// String WRITE = "w";
// }

/** The screen proctoring service client-access API attribute names */
interface SEB_ACCESS {
String ATTR_UUID = "uuid";
Expand Down Expand Up @@ -210,12 +197,30 @@ public GroupSessionCount(
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
static final class GroupName {
@JsonProperty("uuid")
public final String modelId;
@JsonProperty("name")
public final String name;

@JsonCreator
public GroupName(
@JsonProperty("modelId") final String modelId,
@JsonProperty("name") final String name) {

this.modelId = modelId;
this.name = name;
}
}

private final UserDAO userDAO;
private final Cryptor cryptor;
private final AsyncService asyncService;
private final JSONMapper jsonMapper;
private final ProctoringSettingsDAO proctoringSettingsDAO;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final ScreenProctoringGroupDAO screenProctoringGroupDAO;
private final WebserviceInfo webserviceInfo;

ScreenProctoringAPIBinding(
Expand All @@ -225,6 +230,7 @@ public GroupSessionCount(
final JSONMapper jsonMapper,
final ProctoringSettingsDAO proctoringSettingsDAO,
final AdditionalAttributesDAO additionalAttributesDAO,
final ScreenProctoringGroupDAO screenProctoringGroupDAO,
final WebserviceInfo webserviceInfo) {

this.userDAO = userDAO;
Expand All @@ -233,6 +239,7 @@ public GroupSessionCount(
this.jsonMapper = jsonMapper;
this.proctoringSettingsDAO = proctoringSettingsDAO;
this.additionalAttributesDAO = additionalAttributesDAO;
this.screenProctoringGroupDAO = screenProctoringGroupDAO;
this.webserviceInfo = webserviceInfo;
}

Expand Down Expand Up @@ -446,6 +453,46 @@ void deleteSPSUser(final String userUUID) {
}
}

public void synchronizeGroups(final Exam exam) {
try {

// TODO try to sync groups for exam with SPS Service.
// If some group on SPS service has been delete,
// delete (or mark) it also locally

// Set<String> localGroupIds = this.screenProctoringGroupDAO
// .getCollectingGroups(exam.id)
// .getOrThrow()
// .stream()
// .map(g -> g.uuid)
// .collect(Collectors.toSet());
//
// final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
// final SPSData spsData = this.getSPSData(exam.id);
// final String groupRequestURI = UriComponentsBuilder
// .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
// .path(SPS_API.GROUP_ENDPOINT + "/names")
// .queryParam(SPS_API.GROUP.ATTR_UUID, exam.externalId)
// .build()
// .toUriString();
// final ResponseEntity<String> exchangeGroups = apiTemplate.exchange(groupRequestURI, HttpMethod.GET);
//
// final Collection<GroupName> groups = this.jsonMapper.readValue(
// exchangeGroups.getBody(),
// new TypeReference<Collection<GroupName>>() {
// });
//
// for (final GroupName group : groups) {
// if (!localGroupIds.contains(group.modelId)) {
// System.out.println("************* TODO delete local group");
// }
// }

} catch (final Exception e) {
log.error("Failed to synchronize groups for exam: {} error: {}", exam.name, e.getMessage());
}
}

/** This is called when an exam has changed its parameter and needs data update on SPS side
*
* @param exam The exam*/
Expand Down Expand Up @@ -830,10 +877,12 @@ public Collection<GroupSessionCount> getActiveGroupSessionCounts() {
return Collections.emptyList();
}

return this.jsonMapper.readValue(
Collection<GroupSessionCount> groupSessionCounts = this.jsonMapper.readValue(
exchange.getBody(),
new TypeReference<Collection<GroupSessionCount>>() {
});

return groupSessionCounts;

} catch (final Exception e) {
log.error("Failed to get active group session counts: {}", e.getMessage());
Expand Down Expand Up @@ -904,7 +953,8 @@ private static UserMod getUserModifications(final UserInfo userInfo, final SEBSe
final Set<String> spsUserRoles = new HashSet<>();
spsUserRoles.add(SPS_API.SPSUserRole.PROCTOR.name());
if (userInfo.roles.contains(UserRole.SEB_SERVER_ADMIN.name()) ||
userInfo.roles.contains(UserRole.INSTITUTIONAL_ADMIN.name())) {
userInfo.roles.contains(UserRole.INSTITUTIONAL_ADMIN.name()) ||
userInfo.roles.contains(UserRole.EXAM_ADMIN.name())) {
spsUserRoles.add(SPS_API.SPSUserRole.ADMIN.name());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public ScreenProctoringServiceImpl(
jsonMapper,
proctoringSettingsDAO,
additionalAttributesDAO,
screenProctoringGroupDAO,
webserviceInfo);
}

Expand Down Expand Up @@ -237,6 +238,7 @@ private Exam createExamGroups(final Exam exam, final Collection<ScreenProctoring
return exam;
}

// TODO make sense to cache here?
@Override
public Result<Collection<ScreenProctoringGroup>> getCollectingGroups(final Long examId) {
return this.screenProctoringGroupDAO.getCollectingGroups(examId);
Expand Down Expand Up @@ -306,6 +308,7 @@ public void notifyExamSaved(final Exam exam) {

this.screenProctoringAPIBinding.synchronizeUserAccounts(exam);
this.screenProctoringAPIBinding.updateExam(exam);
this.screenProctoringAPIBinding.synchronizeGroups(exam);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
Expand Down Expand Up @@ -179,8 +180,11 @@ protected Result<UserInfo> validForSave(final UserInfo userInfo) {
}

@Override
protected Result<UserInfo> notifySaved(final UserInfo entity) {
final Result<UserInfo> userInfoResult = super.notifySaved(entity);
protected Result<UserInfo> notifySaved(
final UserInfo entity,
final Activatable.ActivationAction activationAction) {

final Result<UserInfo> userInfoResult = super.notifySaved(entity, activationAction);
this.synchronizeUserWithSPS(entity);
return userInfoResult;
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/resources/config/application-dev.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
sebserver.test.property=This is the development Setup

server.address=129.132.182.122
server.address=localhost
server.port=8080
server.servlet.context-path=/
server.tomcat.uri-encoding=UTF-8
Expand All @@ -17,7 +17,7 @@ logging.level.ch.ethz.seb.sebserver.webservice.servicelayer=DEBUG
sebserver.gui.http.external.scheme=http
sebserver.gui.entrypoint=/gui
sebserver.gui.webservice.protocol=http
sebserver.gui.webservice.address=129.132.182.122
sebserver.gui.webservice.address=localhost
sebserver.gui.webservice.port=8080
sebserver.gui.webservice.apipath=/admin-api/v1
# defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page
Expand Down Expand Up @@ -66,7 +66,7 @@ sebserver.webservice.light.setup=false
sebserver.webservice.distributed=false
#sebserver.webservice.master.delay.threshold=10000
sebserver.webservice.http.external.scheme=http
sebserver.webservice.http.external.servername=129.132.182.122
sebserver.webservice.http.external.servername=localhost
sebserver.webservice.http.external.port=${server.port}
sebserver.webservice.http.redirect.gui=/gui
sebserver.webservice.ping.service.strategy=BATCH
Expand Down Expand Up @@ -110,7 +110,7 @@ springdoc.paths-to-exclude=/exam-api,/exam-api/discovery,/sebserver/error,/sebse

sebserver.feature.exam.seb.screenProctoring.enabled=true
sebserver.feature.exam.seb.screenProctoring.bundled=true
sebserver.feature.exam.seb.screenProctoring.bundled.url=http://129.132.182.122:8090
sebserver.feature.exam.seb.screenProctoring.bundled.url=http://localhost:8090
sebserver.feature.exam.seb.screenProctoring.bundled.clientId=sebserverClient
sebserver.feature.exam.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount

Expand Down

0 comments on commit c730bc9

Please sign in to comment.