SEBSERV-559 and SEBSERV-552

This commit is contained in:
anhefti 2024-07-03 16:29:00 +02:00
parent c0ead99e2b
commit 16b2c8deb4
12 changed files with 131 additions and 90 deletions

View file

@ -169,7 +169,6 @@ public class MonitoringProctoringService {
final ScreenProctoringSettings screenProctoringSettings) { final ScreenProctoringSettings screenProctoringSettings) {
collectingRooms collectingRooms
.stream()
.forEach(room -> updateProctoringAction( .forEach(room -> updateProctoringAction(
pageContext, pageContext,
proctoringSettings, proctoringSettings,
@ -184,7 +183,6 @@ public class MonitoringProctoringService {
if (screenProctoringGroups != null) { if (screenProctoringGroups != null) {
screenProctoringGroups screenProctoringGroups
.stream()
.forEach(group -> updateScreenProctoringAction( .forEach(group -> updateScreenProctoringAction(
pageContext, pageContext,
screenProctoringSettings, screenProctoringSettings,
@ -357,8 +355,12 @@ public class MonitoringProctoringService {
// Open SPS Gui redirect URL with login token (jwt token) in new browser tab // Open SPS Gui redirect URL with login token (jwt token) in new browser tab
final String redirectLocation = redirect.getBody() + "/jwt?token=" + tokenRequest.getBody(); final String redirectLocation = redirect.getBody() + "/jwt?token=" + tokenRequest.getBody();
final String script = "window.open("+ redirectLocation + ", 'seb_screen_proctoring')";
final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class); final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
launcher.openURL(redirectLocation); launcher.openURL(redirectLocation);
// RWT.getClient()
// .getService(JavaScriptExecutor.class)
// .execute(script);
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to open screen proctoring service group gallery view: ", e); log.error("Failed to open screen proctoring service group gallery view: ", e);
_action.pageContext() _action.pageContext()

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019 ETH Zürich, IT Services
*
* 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.servicelayer.authorization;
public final class AdHocAccountData {
public final String userId;
public final String username;
public final String userMail;
public final String firstName;
public final String lastName;
public final String timezone;
public AdHocAccountData(
final String userId,
final String username,
final String userMail,
final String firstName,
final String lastName,
final String timezone) {
this.userId = userId;
this.username = username;
this.userMail = userMail;
this.firstName = firstName;
this.lastName = lastName;
this.timezone = timezone;
}
}

View file

@ -27,7 +27,7 @@ public interface TeacherAccountService {
*/ */
Result<UserInfo> createNewTeacherAccountForExam( Result<UserInfo> createNewTeacherAccountForExam(
Exam exam, Exam exam,
final FullLmsIntegrationService.AdHocAccountData adHocAccountData); final AdHocAccountData adHocAccountData);
/** Get the identifier for certain Teacher account for specified Exam. /** Get the identifier for certain Teacher account for specified Exam.
* *
@ -37,7 +37,7 @@ public interface TeacherAccountService {
*/ */
default String getTeacherAccountIdentifier( default String getTeacherAccountIdentifier(
final Exam exam, final Exam exam,
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) { final AdHocAccountData adHocAccountData) {
return getTeacherAccountIdentifier( return getTeacherAccountIdentifier(
String.valueOf(exam.lmsSetupId), String.valueOf(exam.lmsSetupId),
@ -62,7 +62,7 @@ public interface TeacherAccountService {
*/ */
Result<String> getOneTimeTokenForTeacherAccount( Result<String> getOneTimeTokenForTeacherAccount(
Exam exam, Exam exam,
FullLmsIntegrationService.AdHocAccountData adHocAccountData, AdHocAccountData adHocAccountData,
boolean createIfNotExists); boolean createIfNotExists);
/** Used to verify a given One Time Access JWT Token. This must have the expected claims and must not be expired /** Used to verify a given One Time Access JWT Token. This must have the expected claims and must not be expired

View file

@ -20,6 +20,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServe
public interface UserService { public interface UserService {
String USERS_INSTITUTION_AS_DEFAULT = "USERS_INSTITUTION_AS_DEFAULT"; String USERS_INSTITUTION_AS_DEFAULT = "USERS_INSTITUTION_AS_DEFAULT";
/** UUID of the internal account that is used for LMS integration related remote call tasks */
String LMS_INTEGRATION_CLIENT_UUID = "LMS_INTEGRATION_CLIENT"; String LMS_INTEGRATION_CLIENT_UUID = "LMS_INTEGRATION_CLIENT";
String LMS_INTEGRATION_CLIENT_NAME = "lmsIntegrationClient"; String LMS_INTEGRATION_CLIENT_NAME = "lmsIntegrationClient";

View file

@ -20,10 +20,10 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.TeacherAccountService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.TeacherAccountService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminAPIClientDetails; import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminAPIClientDetails;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
@ -91,7 +91,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
@Override @Override
public Result<UserInfo> createNewTeacherAccountForExam( public Result<UserInfo> createNewTeacherAccountForExam(
final Exam exam, final Exam exam,
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) { final AdHocAccountData adHocAccountData) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
@ -130,7 +130,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
@Override @Override
public Result<String> getOneTimeTokenForTeacherAccount( public Result<String> getOneTimeTokenForTeacherAccount(
final Exam exam, final Exam exam,
final FullLmsIntegrationService.AdHocAccountData adHocAccountData, final AdHocAccountData adHocAccountData,
final boolean createIfNotExists) { final boolean createIfNotExists) {
return this.userDAO return this.userDAO
@ -186,7 +186,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
private UserInfo handleAccountDoesNotExistYet( private UserInfo handleAccountDoesNotExistYet(
final boolean createIfNotExists, final boolean createIfNotExists,
final Exam exam, final Exam exam,
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) { final AdHocAccountData adHocAccountData) {
if (createIfNotExists) { if (createIfNotExists) {
return this return this
@ -212,6 +212,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
"Failed to apply ad-hoc-teacher account to supporter list of exam: {} user: {}", "Failed to apply ad-hoc-teacher account to supporter list of exam: {} user: {}",
exam, account, error)); exam, account, error));
} }
return account; return account;
} }
@ -219,7 +220,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
final String subjectClaim = UUID.randomUUID().toString(); final String subjectClaim = UUID.randomUUID().toString();
userDAO.changePassword(account.uuid, subjectClaim); userDAO.changePassword(account.uuid, subjectClaim);
synchronizeSPSUserForExam(account, examId); this.screenProctoringService.updateExamOnScreenProctoringService(examId);
final Map<String, Object> claims = new HashMap<>(); final Map<String, Object> claims = new HashMap<>();
claims.put(USER_CLAIM, account.uuid); claims.put(USER_CLAIM, account.uuid);
@ -275,10 +276,4 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
return claims; return claims;
} }
private UserInfo synchronizeSPSUserForExam(final UserInfo account, final Long examId) {
if (this.screenProctoringService.isScreenProctoringEnabled(examId)) {
this.screenProctoringService.synchronizeSPSUserForExam(examId);
}
return account;
}
} }

View file

@ -16,7 +16,6 @@ import java.util.Objects;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.*; import ch.ethz.seb.sebserver.gbl.model.exam.*;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
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.joda.time.DateTime;
@ -299,7 +298,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
!Objects.equals(examConfig.templateId, examTemplate.configTemplateId)) { !Objects.equals(examConfig.templateId, examTemplate.configTemplateId)) {
final String newName = (examConfig != null && examConfig.name.equals(configName)) final String newName = (examConfig != null && examConfig.name.equals(configName))
? examConfig.name + "_" ? examConfig.name + "_" + DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_FORMATTER)
: configName; : configName;
final ConfigurationNode config = new ConfigurationNode( final ConfigurationNode config = new ConfigurationNode(

View file

@ -11,12 +11,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collection; import java.util.Collection;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateChangeEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateChangeEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent;
@ -26,7 +25,6 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.RequestParam;
public interface FullLmsIntegrationService { public interface FullLmsIntegrationService {
@ -79,30 +77,7 @@ public interface FullLmsIntegrationService {
AdHocAccountData adHocAccountData); AdHocAccountData adHocAccountData);
final class AdHocAccountData {
public final String userId;
public final String username;
public final String userMail;
public final String firstName;
public final String lastName;
public final String timezone;
public AdHocAccountData(
final String userId,
final String username,
final String userMail,
final String firstName,
final String lastName,
final String timezone) {
this.userId = userId;
this.username = username;
this.userMail = userMail;
this.firstName = firstName;
this.lastName = lastName;
this.timezone = timezone;
}
}
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
final class ExamData { final class ExamData {

View file

@ -32,6 +32,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
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.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData;
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.authorization.impl.SEBServerUser; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.TeacherAccountServiceImpl; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.TeacherAccountServiceImpl;
@ -455,14 +456,22 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
final String quizId, final String quizId,
final String examData) { final String examData) {
final String internalQuizId = MoodleUtils.getInternalQuizId( if (StringUtils.isNotBlank(examData)) {
quizId, return lmsAPITemplate
courseId, .getQuizDataForRemoteImport(examData)
null, .getOrThrow();
null); } else {
return lmsAPITemplate.getQuizDataForRemoteImport(examData) final String internalQuizId = MoodleUtils.getInternalQuizId(
.getOrThrow(); quizId,
courseId,
null,
null);
return lmsAPITemplate.getQuiz(internalQuizId)
.getOrThrow();
}
} }

View file

@ -71,6 +71,9 @@ public interface ScreenProctoringService extends SessionUpdateTask {
@EventListener(ExamFinishedEvent.class) @EventListener(ExamFinishedEvent.class)
void notifyExamFinished(ExamFinishedEvent event); void notifyExamFinished(ExamFinishedEvent event);
@EventListener(ExamResetEvent.class)
void notifyExamReset(ExamResetEvent event);
/** This is being called just before an Exam gets deleted on the permanent storage. /** This is being called just before an Exam gets deleted on the permanent storage.
* This deactivates and dispose or deletes all exam relevant domain entities on the SPS service side. * This deactivates and dispose or deletes all exam relevant domain entities on the SPS service side.
* *

View file

@ -102,6 +102,12 @@ class ScreenProctoringAPIBinding {
String ATTR_PRIVILEGES = "privileges"; 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 */ /** The screen proctoring service client-access API attribute names */
interface SEB_ACCESS { interface SEB_ACCESS {
String ATTR_UUID = "uuid"; String ATTR_UUID = "uuid";
@ -380,8 +386,6 @@ class ScreenProctoringAPIBinding {
void synchronizeUserAccounts(final Exam exam) { void synchronizeUserAccounts(final Exam exam) {
try { try {
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
final SPSData spsData = this.getSPSData(exam.id);
exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate)); exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate));
if (exam.owner != null) { if (exam.owner != null) {
@ -389,7 +393,7 @@ class ScreenProctoringAPIBinding {
} }
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to synchronize user accounts with SPS for exam: {}", exam); log.error("Failed to synchronize user accounts with SPS for exam: {}", exam, e);
} }
} }
void deleteSPSUser(final String userUUID) { void deleteSPSUser(final String userUUID) {
@ -433,11 +437,7 @@ class ScreenProctoringAPIBinding {
.build() .build()
.toUriString(); .toUriString();
final List<String> userIds = new ArrayList<>(exam.supporter); final List<String> supporterIds = getSupporterIds(exam);
if (exam.owner != null) {
userIds.add(exam.owner);
}
final ExamUpdate examUpdate = new ExamUpdate( final ExamUpdate examUpdate = new ExamUpdate(
exam.name, exam.name,
exam.getDescription(), exam.getDescription(),
@ -445,7 +445,7 @@ class ScreenProctoringAPIBinding {
exam.getType().name(), exam.getType().name(),
exam.startTime != null ? exam.startTime.getMillis() : null, exam.startTime != null ? exam.startTime.getMillis() : null,
exam.endTime != null ? exam.endTime.getMillis() : null, exam.endTime != null ? exam.endTime.getMillis() : null,
userIds); supporterIds);
final String jsonExamUpdate = this.jsonMapper.writeValueAsString(examUpdate); final String jsonExamUpdate = this.jsonMapper.writeValueAsString(examUpdate);
@ -796,17 +796,15 @@ class ScreenProctoringAPIBinding {
if (activityRequest.getStatusCode() != HttpStatus.OK) { if (activityRequest.getStatusCode() != HttpStatus.OK) {
final String body = activityRequest.getBody(); final String body = activityRequest.getBody();
if (body != null && body.contains("Activation argument mismatch")) { if (body != null && !body.contains("Activation argument mismatch")) {
return; log.warn("Failed to synchronize activity for user account on SPS: {}", activityRequest);
} }
log.warn("Failed to synchronize activity for user account on SPS: {}", activityRequest);
} else { } else {
log.info("Successfully synchronize activity for user account on SPS for user: {}", userUUID); log.info("Successfully synchronize activity for user account on SPS for user: {}", userUUID);
} }
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to synchronize user account with SPS for user: {}", userUUID); log.error("Failed to synchronize user account with SPS for user: {}", userUUID, e);
} }
} }
@ -931,18 +929,14 @@ class ScreenProctoringAPIBinding {
try { try {
final List<String> userIds = new ArrayList<>(exam.supporter); final List<String> supporterIds = getSupporterIds(exam);
if (exam.owner != null) {
userIds.add(exam.owner);
}
final String uri = UriComponentsBuilder final String uri = UriComponentsBuilder
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL()) .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
.path(SPS_API.EXAM_ENDPOINT) .path(SPS_API.EXAM_ENDPOINT)
.build().toUriString(); .build().toUriString();
final String uuid = createExamUUID(exam); final String uuid = createExamUUID(exam);
final MultiValueMap<String, String> params = createExamCreationParams(exam, uuid, userIds); final MultiValueMap<String, String> params = createExamCreationParams(exam, uuid, supporterIds);
final String paramsFormEncoded = Utils.toAppFormUrlEncodedBodyForSPService(params); final String paramsFormEncoded = Utils.toAppFormUrlEncodedBodyForSPService(params);
final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded); final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded);
@ -976,7 +970,7 @@ class ScreenProctoringAPIBinding {
private static MultiValueMap<String, String> createExamCreationParams( private static MultiValueMap<String, String> createExamCreationParams(
final Exam exam, final Exam exam,
final String uuid, final String uuid,
final List<String> userIds) { final List<String> supporterIds) {
final MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add(SPS_API.EXAM.ATTR_UUID, uuid); params.add(SPS_API.EXAM.ATTR_UUID, uuid);
@ -985,8 +979,8 @@ class ScreenProctoringAPIBinding {
params.add(SPS_API.EXAM.ATTR_DESCRIPTION, exam.getDescription()); params.add(SPS_API.EXAM.ATTR_DESCRIPTION, exam.getDescription());
} }
params.add(SPS_API.EXAM.ATTR_URL, exam.getStartURL()); params.add(SPS_API.EXAM.ATTR_URL, exam.getStartURL());
if (!userIds.isEmpty()) { if (!supporterIds.isEmpty()) {
params.add(SPS_API.EXAM.ATTR_USER_IDS, StringUtils.join(userIds, Constants.LIST_SEPARATOR)); params.add(SPS_API.EXAM.ATTR_USER_IDS, StringUtils.join(supporterIds, Constants.LIST_SEPARATOR));
} }
params.add(SPS_API.EXAM.ATTR_TYPE, exam.getType().name()); params.add(SPS_API.EXAM.ATTR_TYPE, exam.getType().name());
params.add(SPS_API.EXAM.ATTR_START_TIME, String.valueOf(exam.startTime.getMillis())); params.add(SPS_API.EXAM.ATTR_START_TIME, String.valueOf(exam.startTime.getMillis()));
@ -1199,6 +1193,14 @@ class ScreenProctoringAPIBinding {
return this.apiTemplate; return this.apiTemplate;
} }
private static List<String> getSupporterIds(final Exam exam) {
final List<String> supporterIds = new ArrayList<>(exam.supporter);
if (exam.owner != null && !UserService.LMS_INTEGRATION_CLIENT_UUID.equals(exam.owner)) {
supporterIds.add(exam.owner);
}
return supporterIds;
}
final static class ScreenProctoringServiceOAuthTemplate { final static class ScreenProctoringServiceOAuthTemplate {
private static final String GRANT_TYPE = "password"; private static final String GRANT_TYPE = "password";

View file

@ -17,6 +17,7 @@ import ch.ethz.seb.sebserver.gbl.model.Activatable;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.*;
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.slf4j.Logger; import org.slf4j.Logger;
@ -47,10 +48,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ScreenProctoringGroupDA
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ScreenProctoringGroupDAOImpl.AllGroupsFullException; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ScreenProctoringGroupDAOImpl.AllGroupsFullException;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamStartedEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ScreenProctoringAPIBinding.SPSData; import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ScreenProctoringAPIBinding.SPSData;
@ -270,7 +267,8 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
@Override @Override
public void synchronizeSPSUserForExam(final Long examId) { public void synchronizeSPSUserForExam(final Long examId) {
this.examDAO.byPK(examId) this.examDAO
.byPK(examId)
.onSuccess(this.screenProctoringAPIBinding::synchronizeUserAccounts) .onSuccess(this.screenProctoringAPIBinding::synchronizeUserAccounts)
.onError(error -> log.error("Failed to synchronize SPS user accounts for exam: {}", examId, error)); .onError(error -> log.error("Failed to synchronize SPS user accounts for exam: {}", examId, error));
} }
@ -299,14 +297,26 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
@Override @Override
public void notifyExamFinished(final ExamFinishedEvent event) { public void notifyExamFinished(final ExamFinishedEvent event) {
final Exam exam = event.exam;
if (!this.isScreenProctoringEnabled(exam.id) || if (!this.isScreenProctoringEnabled(event.exam.id) ||
!BooleanUtils.toBoolean(exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) { !BooleanUtils.toBoolean(event.exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) {
return; return;
} }
if (exam.status == Exam.ExamStatus.FINISHED) { if (event.exam.status != Exam.ExamStatus.UP_COMING) {
this.screenProctoringAPIBinding.deactivateScreenProctoring(exam); this.screenProctoringAPIBinding.deactivateScreenProctoring(event.exam);
}
}
@Override
public void notifyExamReset(final ExamResetEvent event) {
if (!this.isScreenProctoringEnabled(event.exam.id) ||
BooleanUtils.toBoolean(event.exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) {
return;
}
if (event.exam.status != Exam.ExamStatus.UP_COMING) {
this.screenProctoringAPIBinding.activateScreenProctoring(event.exam);
} }
} }

View file

@ -21,6 +21,8 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.NoResourceFoundException;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -89,15 +91,23 @@ public class LmsIntegrationController {
examData) examData)
.onError(e -> { .onError(e -> {
log.error( log.error(
"Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {} error: {}", "Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {} error: ",
lmsUUId, courseId, quizId, templateId, e.getMessage()); lmsUUId, courseId, quizId, templateId, e);
log.info("Rollback Exam creation..."); log.info("Rollback Exam creation...");
fullLmsIntegrationService.deleteExam(lmsUUId, courseId, quizId) fullLmsIntegrationService.deleteExam(lmsUUId, courseId, quizId)
.onError(error -> log.error("Failed to rollback auto Exam import: ", error)); .onError(error -> {
if (error instanceof NoResourceFoundException) {
log.info("No Exam found for rollback.");
} else {
log.error("Failed to rollback auto Exam import: ", error);
}
});
}) })
.getOrThrow(); .getOr(null);
log.info("Auto import of exam successful: {}", exam); if (exam != null) {
log.info("Auto import of exam successful: {}", exam);
}
} }
@RequestMapping( @RequestMapping(
@ -181,7 +191,7 @@ public class LmsIntegrationController {
@RequestParam(name = API.LMS_FULL_INTEGRATION_TIME_ZONE, required = false) final String timezone, @RequestParam(name = API.LMS_FULL_INTEGRATION_TIME_ZONE, required = false) final String timezone,
final HttpServletResponse response) { final HttpServletResponse response) {
final FullLmsIntegrationService.AdHocAccountData adHocAccountData = new FullLmsIntegrationService.AdHocAccountData( final AdHocAccountData adHocAccountData = new AdHocAccountData(
userId, userId,
username, username,
userMail, userMail,