SEBSERV-490 join proctoring in Ready state only for Exam without LMS

This commit is contained in:
anhefti 2023-12-21 16:02:51 +01:00
parent cfa9fba9cb
commit 74d4b1ff7d
4 changed files with 62 additions and 45 deletions

View file

@ -251,7 +251,9 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
final Collection<ClientConnectionRecord> records = this.clientConnectionRecordMapper
.selectByExample()
.where(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomUpdate, isNotEqualTo(0))
.and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name()))
.and(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomId, isNull())
.and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name()),
or(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.READY.name())))
.build()
.execute();
@ -292,7 +294,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
.build()
.execute()
.stream()
.map(r -> r.getConnectionToken())
.map(ClientConnectionRecord::getConnectionToken)
.collect(Collectors.toList());
});
}
@ -305,6 +307,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
.selectByExample()
.where(ClientConnectionRecordDynamicSqlSupport.remoteProctoringRoomUpdate, isNotEqualTo(0))
.and(ClientConnectionRecordDynamicSqlSupport.status, isNotEqualTo(ConnectionStatus.ACTIVE.name()))
.and(ClientConnectionRecordDynamicSqlSupport.status, isNotEqualTo(ConnectionStatus.READY.name()))
.build()
.execute();
@ -551,7 +554,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
.selectByExample()
.where(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupUpdate, isNotEqualTo((byte) 0))
.and(ClientConnectionRecordDynamicSqlSupport.examId, isIn(examIds))
.and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name()))
.and(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.ACTIVE.name()),
or(ClientConnectionRecordDynamicSqlSupport.status, isEqualTo(ConnectionStatus.READY.name())))
.build()
.execute();
@ -588,7 +592,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
this.clientConnectionRecordMapper::update,
ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
.set(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupId).equalTo(groupId)
//.set(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupUpdate).equalTo((byte) 0)
.set(ClientConnectionRecordDynamicSqlSupport.screenProctoringGroupUpdate).equalTo((byte) 0)
.where(ClientConnectionRecordDynamicSqlSupport.id, isEqualTo(connectionId))
.build()
.execute();

View file

@ -66,7 +66,7 @@ public interface RemoteProctoringRoomService extends SessionUpdateTask {
* New client connections that are coming in and are established only mark itself for
* proctoring room update if proctoring is enabled for the specified exam. This batch processing
* then makes the update synchronously to keep track on room creation and naming
*
* <p>
* If for a specified exam the town-hall room is active incoming client connection are instructed to
* join the town-hall room. If not, incoming client connection are instructed to join a collecting room. */
void updateProctoringCollectingRooms();

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
@ -58,14 +59,10 @@ public class ExamSessionControlTask implements DisposableBean {
this.pingUpdateRate = pingUpdateRate;
this.examUpdateTasks = new ArrayList<>(examUpdateTasks);
this.examUpdateTasks.sort((t1, t2) -> Integer.compare(
t1.examUpdateTaskProcessingOrder(),
t2.examUpdateTaskProcessingOrder()));
this.examUpdateTasks.sort(Comparator.comparingInt(ExamUpdateTask::examUpdateTaskProcessingOrder));
this.sessionUpdateTasks = new ArrayList<>(sessionUpdateTasks);
this.sessionUpdateTasks.sort((t1, t2) -> Integer.compare(
t1.sessionUpdateTaskProcessingOrder(),
t2.sessionUpdateTaskProcessingOrder()));
this.sessionUpdateTasks.sort(Comparator.comparingInt(SessionUpdateTask::sessionUpdateTaskProcessingOrder));
}
@EventListener(SEBServerInitEvent.class)

View file

@ -19,6 +19,8 @@ import java.util.stream.Stream;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -258,16 +260,18 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
clientConnection);
final ConnectionStatus currentStatus = clientConnection.getStatus();
final ConnectionStatus newStatus = (StringUtils.isNotBlank(userSessionId) && currentStatus == ConnectionStatus.READY)
? ConnectionStatus.ACTIVE
: currentStatus;
final String signatureHash = StringUtils.isNotBlank(appSignatureKey)
? getSignatureHash(appSignatureKey, connectionToken, _examId)
: null;
final ClientConnection updateConnection = new ClientConnection(
clientConnection.id,
null,
examId,
(StringUtils.isNotBlank(userSessionId) && currentStatus == ConnectionStatus.READY)
? ConnectionStatus.ACTIVE
: null,
newStatus,
null,
updateUserSessionId,
StringUtils.isNotBlank(clientAddress) ? clientAddress : null,
@ -280,9 +284,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
null,
null,
null,
applyScreenProctoring(_examId, newStatus),
null,
null,
null,
applyProctoring(_examId, newStatus),
null,
signatureHash,
null);
@ -311,6 +315,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
});
}
@Override
public Result<ClientConnection> establishClientConnection(
final String connectionToken,
@ -331,12 +337,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
connectionStatusIntegrityCheck(clientConnection, clientAddress);
checkInstitutionalIntegrity(institutionId, clientConnection);
checkExamIntegrity(
examId,
clientConnection.examId,
institutionId,
clientConnection.connectionToken,
clientConnection.info);
checkExamIntegrity(examId, clientConnection.examId, institutionId, clientConnection.connectionToken, clientConnection.info);
if (log.isDebugEnabled()) {
log.debug(
@ -350,28 +351,17 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
clientId,
sebMachineName,
clientConnection);
final Boolean proctoringEnabled = this.examAdminService
.isProctoringEnabled(clientConnection.examId)
.getOr(false);
final Boolean screenProctoringEnabled = this.examAdminService
.isScreenProctoringEnabled(clientConnection.examId)
.getOr(false);
final Long currentExamId = (examId != null) ? examId : clientConnection.examId;
final String currentVdiConnectionId = (clientId != null)
? clientId
: clientConnection.sebClientUserId;
final ConnectionStatus newStatus = StringUtils.isNotBlank(userSessionId) || alreadyAuthenticated(clientConnection)
? ConnectionStatus.ACTIVE
: ConnectionStatus.READY;
// create new ClientConnection for update
final ClientConnection establishedClientConnection = new ClientConnection(
clientConnection.id,
null,
currentExamId,
StringUtils.isNotBlank(userSessionId) || alreadyAuthenticated(clientConnection)
? ConnectionStatus.ACTIVE
: ConnectionStatus.READY,
_examId,
newStatus,
null,
updateUserSessionId,
StringUtils.isNotBlank(clientAddress) ? clientAddress : null,
@ -384,9 +374,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
null,
null,
null,
screenProctoringEnabled,
applyScreenProctoring(_examId, newStatus),
null,
proctoringEnabled,
applyProctoring(_examId, newStatus),
null,
getSignatureHash(appSignatureKey, connectionToken, _examId),
null);
@ -394,7 +384,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
// ClientConnection integrity check
if (clientConnection.institutionId == null ||
clientConnection.connectionToken == null ||
currentExamId == null ||
_examId == null ||
(clientConnection.clientAddress == null && clientAddress == null)) {
log.error("ClientConnection integrity violation, clientConnection: {}, updatedClientConnection: {}",
@ -835,10 +825,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
if (this.isDistributedSetup) {
// if the cached Exam is not up-to-date anymore, we have to update the cache first
final Result<Exam> updateExamCache = this.examSessionService.updateExamCache(examId);
if (updateExamCache.hasError()) {
log.warn("Failed to update Exam-Cache for Exam: {}", examId);
}
this.examSessionService
.updateExamCache(examId)
.onError(error -> log.warn("Failed to update Exam-Cache for Exam: {}", examId));
}
if (currentExamId != null && !examId.equals(currentExamId)) {
@ -922,4 +911,31 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
!clientConnection.userSessionId.equals(clientConnection.sebClientUserId) &&
!clientConnection.userSessionId.equals(clientConnection.sebMachineName);
}
private boolean applyProctoring(final Long examId, final ConnectionStatus status) {
if (examId == null) {
return false;
}
final Exam exam = this.examSessionCacheService.getRunningExam(examId);
final boolean proctoringEnabled = exam != null && BooleanUtils.toBoolean(
exam.getAdditionalAttribute(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING));
return isApplyProctoring(status, exam) && proctoringEnabled;
}
private boolean applyScreenProctoring(final Long examId, final ConnectionStatus status) {
if (examId == null) {
return false;
}
final Exam exam = this.examSessionCacheService.getRunningExam(examId);
final boolean screenProctoringEnabled = exam != null && BooleanUtils.toBoolean(
exam.getAdditionalAttribute(ScreenProctoringSettings.ATTR_ENABLE_SCREEN_PROCTORING));
return isApplyProctoring(status, exam) && screenProctoringEnabled;
}
private static boolean isApplyProctoring(final ConnectionStatus status, final Exam exam) {
return (exam != null && exam.lmsSetupId == null && status == ConnectionStatus.READY) ||
status == ConnectionStatus.ACTIVE;
}
}