refactor proctoring
This commit is contained in:
parent
ad7f06e521
commit
39f4c85d22
13 changed files with 368 additions and 398 deletions
|
@ -52,6 +52,13 @@ public interface ClientConnectionDAO extends
|
||||||
return getConnectionTokens(examId);
|
return getConnectionTokens(examId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get a list of all connection tokens of all connections of an exam
|
||||||
|
* that are in state active
|
||||||
|
*
|
||||||
|
* @param examId The exam identifier
|
||||||
|
* @return Result refer to the collection of connection tokens or to an error when happened */
|
||||||
|
Result<Collection<String>> getActiveConnctionTokens(Long examId);
|
||||||
|
|
||||||
/** Get a collection of all client connections records that needs a room update
|
/** Get a collection of all client connections records that needs a room update
|
||||||
* and that are in the status ACTIVE.
|
* and that are in the status ACTIVE.
|
||||||
* This also flags the involved connections for no update needed within the
|
* This also flags the involved connections for no update needed within the
|
||||||
|
|
|
@ -146,6 +146,25 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<String>> getActiveConnctionTokens(final Long examId) {
|
||||||
|
return Result.tryCatch(() -> this.clientConnectionRecordMapper
|
||||||
|
.selectByExample()
|
||||||
|
.where(
|
||||||
|
ClientConnectionRecordDynamicSqlSupport.examId,
|
||||||
|
SqlBuilder.isEqualTo(examId))
|
||||||
|
.and(
|
||||||
|
ClientConnectionRecordDynamicSqlSupport.status,
|
||||||
|
SqlBuilder.isEqualTo(ConnectionStatus.ACTIVE.name()))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.map(ClientConnectionRecord::getConnectionToken)
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Result<Collection<ClientConnectionRecord>> getAllConnectionIdsForRoomUpdateActive() {
|
public Result<Collection<ClientConnectionRecord>> getAllConnectionIdsForRoomUpdateActive() {
|
||||||
|
|
|
@ -45,25 +45,25 @@ public interface ExamAdminService {
|
||||||
*
|
*
|
||||||
* @param examId the exam instance
|
* @param examId the exam instance
|
||||||
* @return Result refer to ExamProctoring data for the exam. */
|
* @return Result refer to ExamProctoring data for the exam. */
|
||||||
default Result<ProctoringSettings> getExamProctoring(final Exam exam) {
|
default Result<ProctoringSettings> getExamProctoringSettings(final Exam exam) {
|
||||||
if (exam == null || exam.id == null) {
|
if (exam == null || exam.id == null) {
|
||||||
return Result.ofRuntimeError("Invalid Exam model");
|
return Result.ofRuntimeError("Invalid Exam model");
|
||||||
}
|
}
|
||||||
return getExamProctoring(exam.id);
|
return getExamProctoringSettings(exam.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get ExamProctoring data for a certain exam to an error when happened.
|
/** Get ExamProctoring data for a certain exam to an error when happened.
|
||||||
*
|
*
|
||||||
* @param examId the exam identifier
|
* @param examId the exam identifier
|
||||||
* @return Result refer to ExamProctoring data for the exam. */
|
* @return Result refer to ExamProctoring data for the exam. */
|
||||||
Result<ProctoringSettings> getExamProctoring(Long examId);
|
Result<ProctoringSettings> getExamProctoringSettings(Long examId);
|
||||||
|
|
||||||
/** Save the given ExamProctoring data for an existing Exam.
|
/** Save the given ExamProctoring data for an existing Exam.
|
||||||
*
|
*
|
||||||
* @param examId the exam identifier
|
* @param examId the exam identifier
|
||||||
* @param examProctoring The ExamProctoring data to save for the exam
|
* @param examProctoring The ExamProctoring data to save for the exam
|
||||||
* @return Result refer to saved ExamProctoring data or to an error when happened. */
|
* @return Result refer to saved ExamProctoring data or to an error when happened. */
|
||||||
Result<ProctoringSettings> saveExamProctoring(Long examId, ProctoringSettings examProctoring);
|
Result<ProctoringSettings> saveExamProctoringSettings(Long examId, ProctoringSettings examProctoring);
|
||||||
|
|
||||||
/** This indicates if proctoring is set and enabled for a certain exam.
|
/** This indicates if proctoring is set and enabled for a certain exam.
|
||||||
*
|
*
|
||||||
|
@ -85,7 +85,25 @@ public interface ExamAdminService {
|
||||||
/** Get the exam proctoring service implementation of specified type.
|
/** Get the exam proctoring service implementation of specified type.
|
||||||
*
|
*
|
||||||
* @param type exam proctoring service server type
|
* @param type exam proctoring service server type
|
||||||
* @return Result refer to the ExamProctoringService or to an error when happened */
|
* @return ExamProctoringService instance */
|
||||||
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type);
|
Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type);
|
||||||
|
|
||||||
|
/** Get the exam proctoring service implementation of specified type.
|
||||||
|
*
|
||||||
|
* @param settings the ProctoringSettings that defines the ProctoringServerType
|
||||||
|
* @return ExamProctoringService instance */
|
||||||
|
default Result<ExamProctoringService> getExamProctoringService(final ProctoringSettings settings) {
|
||||||
|
return Result.tryCatch(() -> getExamProctoringService(settings.serverType).getOrThrow());
|
||||||
|
}
|
||||||
|
|
||||||
|
default Result<ExamProctoringService> getExamProctoringService(final Exam exam) {
|
||||||
|
return Result.tryCatch(() -> getExamProctoringService(exam.id).getOrThrow());
|
||||||
|
}
|
||||||
|
|
||||||
|
default Result<ExamProctoringService> getExamProctoringService(final Long examId) {
|
||||||
|
return getExamProctoringSettings(examId)
|
||||||
|
.flatMap(this::getExamProctoringService);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<ProctoringSettings> getExamProctoring(final Long examId) {
|
public Result<ProctoringSettings> getExamProctoringSettings(final Long examId) {
|
||||||
return this.additionalAttributesDAO.getAdditionalAttributes(EntityType.EXAM, examId)
|
return this.additionalAttributesDAO.getAdditionalAttributes(EntityType.EXAM, examId)
|
||||||
.map(attrs -> attrs.stream()
|
.map(attrs -> attrs.stream()
|
||||||
.collect(Collectors.toMap(
|
.collect(Collectors.toMap(
|
||||||
|
@ -206,7 +206,8 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Result<ProctoringSettings> saveExamProctoring(final Long examId, final ProctoringSettings examProctoring) {
|
public Result<ProctoringSettings> saveExamProctoringSettings(final Long examId,
|
||||||
|
final ProctoringSettings examProctoring) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||||
|
@ -264,7 +265,8 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type) {
|
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type) {
|
||||||
return this.examProctoringServiceFactory.getExamProctoringService(type);
|
return this.examProctoringServiceFactory
|
||||||
|
.getExamProctoringService(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean getEnabled(final Map<String, AdditionalAttributeRecord> mapping) {
|
private Boolean getEnabled(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||||
|
|
|
@ -14,6 +14,8 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
|
||||||
|
/** Defines functionality to deal with proctoring rooms in a generic way (independent from meeting service) */
|
||||||
|
|
||||||
public interface ExamProctoringRoomService {
|
public interface ExamProctoringRoomService {
|
||||||
|
|
||||||
/** Get all existing default proctoring rooms of an exam.
|
/** Get all existing default proctoring rooms of an exam.
|
||||||
|
@ -40,8 +42,10 @@ public interface ExamProctoringRoomService {
|
||||||
* This attaches or detaches client connections from or to proctoring rooms of an exam in one batch.
|
* This attaches or detaches client connections from or to proctoring rooms of an exam in one batch.
|
||||||
* New client connections that are coming in and are established only mark itself for
|
* 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
|
* proctoring room update if proctoring is enabled for the specified exam. This batch processing
|
||||||
* then makes the update synchronous to not create to to many rooms or several rooms with the same
|
* then makes the update synchronously to keep track on room creation and naming
|
||||||
* name of an exam. */
|
*
|
||||||
|
* 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();
|
void updateProctoringCollectingRooms();
|
||||||
|
|
||||||
/** This creates a town-hall room for a specific exam. The exam must be active and running
|
/** This creates a town-hall room for a specific exam. The exam must be active and running
|
||||||
|
|
|
@ -8,11 +8,17 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Base64.Encoder;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
|
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
|
||||||
public interface ExamProctoringService {
|
public interface ExamProctoringService {
|
||||||
|
|
||||||
|
@ -27,49 +33,69 @@ public interface ExamProctoringService {
|
||||||
* @return Result refer to true if the settings are correct and the proctoring server can be accessed. */
|
* @return Result refer to true if the settings are correct and the proctoring server can be accessed. */
|
||||||
Result<Boolean> testExamProctoring(final ProctoringSettings examProctoring);
|
Result<Boolean> testExamProctoring(final ProctoringSettings examProctoring);
|
||||||
|
|
||||||
/** Used to get the proctor's room connection data.
|
Result<SEBProctoringConnection> getProctorRoomConnection(
|
||||||
*
|
ProctoringSettings proctoringSettings,
|
||||||
* @param proctoringSettings the proctoring settings
|
String roomName,
|
||||||
* @param roomName the name of the room
|
String subject);
|
||||||
* @param subject name of the room
|
|
||||||
* @return SEBProctoringConnectionData that contains all connection data */
|
|
||||||
Result<SEBProctoringConnection> createProctorPublicRoomConnection(
|
|
||||||
final ProctoringSettings proctoringSettings,
|
|
||||||
final String roomName,
|
|
||||||
final String subject);
|
|
||||||
|
|
||||||
Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
Result<SEBProctoringConnection> sendJoinRoomToClients(
|
||||||
final ProctoringSettings proctoringSettings,
|
ProctoringSettings proctoringSettings,
|
||||||
final ClientConnection connection);
|
Collection<String> clientConnectionTokens,
|
||||||
|
String roomName,
|
||||||
|
String subject);
|
||||||
|
|
||||||
Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
Result<Void> sendJoinCollectingRoomToClients(
|
||||||
final ProctoringSettings proctoringSettings,
|
ProctoringSettings proctoringSettings,
|
||||||
final String connectionToken,
|
Collection<String> clientConnectionTokens);
|
||||||
final String roomName,
|
|
||||||
final String subject);
|
|
||||||
|
|
||||||
Result<SEBProctoringConnection> getClientRoomConnection(
|
default String verifyRoomName(final String requestedRoomName, final String connectionToken) {
|
||||||
final ProctoringSettings examProctoring,
|
if (StringUtils.isNotBlank(requestedRoomName)) {
|
||||||
final String connectionToken,
|
return requestedRoomName;
|
||||||
final String roomName,
|
}
|
||||||
final String subject);
|
|
||||||
|
|
||||||
Result<SEBProctoringConnection> createProctoringConnection(
|
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
||||||
final ProctoringServerType proctoringServerType,
|
return urlEncoder.encodeToString(
|
||||||
final String connectionToken,
|
Utils.toByteArray(connectionToken));
|
||||||
final String url,
|
}
|
||||||
final String appKey,
|
|
||||||
final CharSequence appSecret,
|
|
||||||
final String clientName,
|
|
||||||
final String clientKey,
|
|
||||||
final String roomName,
|
|
||||||
final String subject,
|
|
||||||
final Long expTime,
|
|
||||||
final boolean moderator);
|
|
||||||
|
|
||||||
Result<String> createClientAccessToken(
|
// /** Used to get the proctor's room connection data.
|
||||||
final ProctoringSettings proctoringSettings,
|
// *
|
||||||
final String connectionToken,
|
// * @param proctoringSettings the proctoring settings
|
||||||
final String roomName);
|
// * @param roomName the name of the room
|
||||||
|
// * @param subject name of the room
|
||||||
|
// * @return SEBProctoringConnectionData that contains all connection data */
|
||||||
|
// Result<SEBProctoringConnection> createProctorPublicRoomConnection(
|
||||||
|
// final ProctoringSettings proctoringSettings,
|
||||||
|
// final String roomName,
|
||||||
|
// final String subject);
|
||||||
|
//
|
||||||
|
// Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
||||||
|
// final ProctoringSettings proctoringSettings,
|
||||||
|
// final ClientConnection connection);
|
||||||
|
//
|
||||||
|
// Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
||||||
|
// final ProctoringSettings proctoringSettings,
|
||||||
|
// final String connectionToken,
|
||||||
|
// final String roomName,
|
||||||
|
// final String subject);
|
||||||
|
//
|
||||||
|
// Result<SEBProctoringConnection> getClientRoomConnection(
|
||||||
|
// final ProctoringSettings examProctoring,
|
||||||
|
// final String connectionToken,
|
||||||
|
// final String roomName,
|
||||||
|
// final String subject);
|
||||||
|
//
|
||||||
|
// Result<SEBProctoringConnection> createProctoringConnection(
|
||||||
|
// final ProctoringServerType proctoringServerType,
|
||||||
|
// final String connectionToken,
|
||||||
|
// final String url,
|
||||||
|
// final String appKey,
|
||||||
|
// final CharSequence appSecret,
|
||||||
|
// final String clientName,
|
||||||
|
// final String clientKey,
|
||||||
|
// final String roomName,
|
||||||
|
// final String subject,
|
||||||
|
// final Long expTime,
|
||||||
|
// final boolean moderator);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,9 +155,12 @@ public interface ExamSessionService {
|
||||||
Long examId,
|
Long examId,
|
||||||
Predicate<ClientConnectionData> filter);
|
Predicate<ClientConnectionData> filter);
|
||||||
|
|
||||||
default Result<Collection<ClientConnectionData>> getAllActiveConnectionData(final Long examId) {
|
/** Gets all connection tokens of active client connection that are related to a specified exam
|
||||||
return getConnectionData(examId, ACTIVE_CONNECTION_DATA_FILTER);
|
* from persistence storage without caching involved.
|
||||||
}
|
*
|
||||||
|
* @param examId the exam identifier
|
||||||
|
* @return Result refer to the collection of connection tokens or to an error when happened. */
|
||||||
|
Result<Collection<String>> getActiveConnectionTokens(final Long examId);
|
||||||
|
|
||||||
/** Use this to check if the current cached running exam is up to date
|
/** Use this to check if the current cached running exam is up to date
|
||||||
* and if not to flush the cache.
|
* and if not to flush the cache.
|
||||||
|
|
|
@ -13,10 +13,16 @@ import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Base64.Encoder;
|
import java.util.Base64.Encoder;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
@ -26,8 +32,9 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
|
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
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;
|
||||||
|
@ -36,12 +43,15 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.Authorization
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Service
|
@Service
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
public class ExamJITSIProctoringService implements ExamProctoringService {
|
public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ExamJITSIProctoringService.class);
|
||||||
|
|
||||||
private static final String JITSI_ACCESS_TOKEN_HEADER =
|
private static final String JITSI_ACCESS_TOKEN_HEADER =
|
||||||
"{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
|
"{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
|
||||||
|
|
||||||
|
@ -51,17 +61,20 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
||||||
private final AuthorizationService authorizationService;
|
private final AuthorizationService authorizationService;
|
||||||
private final ExamSessionService examSessionService;
|
private final ExamSessionService examSessionService;
|
||||||
|
private final SEBClientInstructionService sebClientInstructionService;
|
||||||
private final Cryptor cryptor;
|
private final Cryptor cryptor;
|
||||||
|
|
||||||
protected ExamJITSIProctoringService(
|
protected ExamJITSIProctoringService(
|
||||||
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
|
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
|
||||||
final AuthorizationService authorizationService,
|
final AuthorizationService authorizationService,
|
||||||
final ExamSessionService examSessionService,
|
final ExamSessionService examSessionService,
|
||||||
|
final SEBClientInstructionService sebClientInstructionService,
|
||||||
final Cryptor cryptor) {
|
final Cryptor cryptor) {
|
||||||
|
|
||||||
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
this.examSessionService = examSessionService;
|
this.examSessionService = examSessionService;
|
||||||
|
this.sebClientInstructionService = sebClientInstructionService;
|
||||||
this.cryptor = cryptor;
|
this.cryptor = cryptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +90,124 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<SEBProctoringConnection> createProctorPublicRoomConnection(
|
public Result<SEBProctoringConnection> getProctorRoomConnection(
|
||||||
|
final ProctoringSettings proctoringSettings,
|
||||||
|
final String roomName,
|
||||||
|
final String subject) {
|
||||||
|
|
||||||
|
return this.createProctorPublicRoomConnection(
|
||||||
|
proctoringSettings,
|
||||||
|
roomName,
|
||||||
|
StringUtils.isNoneBlank(subject) ? subject : roomName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<SEBProctoringConnection> sendJoinRoomToClients(
|
||||||
|
final ProctoringSettings proctoringSettings,
|
||||||
|
final Collection<String> clientConnectionTokens,
|
||||||
|
final String roomName,
|
||||||
|
final String subject) {
|
||||||
|
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
clientConnectionTokens
|
||||||
|
.stream()
|
||||||
|
.forEach(connectionToken -> {
|
||||||
|
final SEBProctoringConnection proctoringConnection =
|
||||||
|
getClientRoomConnection(
|
||||||
|
proctoringSettings,
|
||||||
|
connectionToken,
|
||||||
|
verifyRoomName(roomName, connectionToken),
|
||||||
|
(StringUtils.isNotBlank(subject)) ? subject : roomName)
|
||||||
|
.onError(error -> log.error(
|
||||||
|
"Failed to get client room connection data for {} cause: {}",
|
||||||
|
connectionToken,
|
||||||
|
error.getMessage()))
|
||||||
|
.get();
|
||||||
|
if (proctoringConnection != null) {
|
||||||
|
sendJoinInstruction(
|
||||||
|
proctoringSettings.examId,
|
||||||
|
connectionToken,
|
||||||
|
proctoringConnection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return createProctorPublicRoomConnection(
|
||||||
|
proctoringSettings,
|
||||||
|
roomName,
|
||||||
|
(StringUtils.isNotBlank(subject)) ? subject : roomName)
|
||||||
|
.getOrThrow();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Void> sendJoinCollectingRoomToClients(
|
||||||
|
final ProctoringSettings proctoringSettings,
|
||||||
|
final Collection<String> clientConnectionTokens) {
|
||||||
|
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
clientConnectionTokens
|
||||||
|
.stream()
|
||||||
|
.forEach(connectionToken -> {
|
||||||
|
final ClientConnectionData clientConnection = this.examSessionService
|
||||||
|
.getConnectionData(connectionToken)
|
||||||
|
.getOrThrow();
|
||||||
|
final String roomName = this.remoteProctoringRoomDAO
|
||||||
|
.getRoomName(clientConnection.clientConnection.getRemoteProctoringRoomId())
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
final SEBProctoringConnection proctoringConnection = getClientExamCollectingRoomConnection(
|
||||||
|
proctoringSettings,
|
||||||
|
clientConnection.clientConnection.connectionToken,
|
||||||
|
roomName,
|
||||||
|
clientConnection.clientConnection.userSessionId)
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
sendJoinInstruction(
|
||||||
|
proctoringSettings.examId,
|
||||||
|
clientConnection.clientConnection.connectionToken,
|
||||||
|
proctoringConnection);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendJoinInstruction(
|
||||||
|
final Long examId,
|
||||||
|
final String connectionToken,
|
||||||
|
final SEBProctoringConnection proctoringConnection) {
|
||||||
|
|
||||||
|
final Map<String, String> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
attributes.put(
|
||||||
|
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.SERVICE_TYPE,
|
||||||
|
ProctoringSettings.ProctoringServerType.JITSI_MEET.name());
|
||||||
|
attributes.put(
|
||||||
|
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.METHOD,
|
||||||
|
ClientInstruction.ProctoringInstructionMethod.JOIN.name());
|
||||||
|
attributes.put(
|
||||||
|
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_URL,
|
||||||
|
proctoringConnection.serverURL);
|
||||||
|
attributes.put(
|
||||||
|
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_ROOM,
|
||||||
|
proctoringConnection.roomName);
|
||||||
|
if (StringUtils.isNotBlank(proctoringConnection.subject)) {
|
||||||
|
attributes.put(
|
||||||
|
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_ROOM_SUBJECT,
|
||||||
|
proctoringConnection.subject);
|
||||||
|
}
|
||||||
|
attributes.put(
|
||||||
|
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_TOKEN,
|
||||||
|
proctoringConnection.accessToken);
|
||||||
|
|
||||||
|
this.sebClientInstructionService.registerInstruction(
|
||||||
|
examId,
|
||||||
|
InstructionType.SEB_PROCTORING,
|
||||||
|
attributes,
|
||||||
|
connectionToken,
|
||||||
|
true)
|
||||||
|
.onError(error -> log.error("Failed to send join instruction: {}", connectionToken, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<SEBProctoringConnection> createProctorPublicRoomConnection(
|
||||||
final ProctoringSettings proctoringSettings,
|
final ProctoringSettings proctoringSettings,
|
||||||
final String roomName,
|
final String roomName,
|
||||||
final String subject) {
|
final String subject) {
|
||||||
|
@ -99,35 +229,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
||||||
public Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
|
||||||
final ProctoringSettings proctoringSettings,
|
|
||||||
final ClientConnection connection) {
|
|
||||||
|
|
||||||
return Result.tryCatch(() -> {
|
|
||||||
|
|
||||||
final String roomName = this.remoteProctoringRoomDAO
|
|
||||||
.getRoomName(connection.getRemoteProctoringRoomId())
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
return createProctoringConnection(
|
|
||||||
proctoringSettings.serverType,
|
|
||||||
null,
|
|
||||||
proctoringSettings.serverURL,
|
|
||||||
proctoringSettings.appKey,
|
|
||||||
proctoringSettings.getAppSecret(),
|
|
||||||
connection.userSessionId,
|
|
||||||
"seb-client",
|
|
||||||
roomName,
|
|
||||||
connection.userSessionId,
|
|
||||||
forExam(proctoringSettings),
|
|
||||||
false)
|
|
||||||
.getOrThrow();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result<SEBProctoringConnection> getClientExamCollectingRoomConnection(
|
|
||||||
final ProctoringSettings proctoringSettings,
|
final ProctoringSettings proctoringSettings,
|
||||||
final String connectionToken,
|
final String connectionToken,
|
||||||
final String roomName,
|
final String roomName,
|
||||||
|
@ -154,8 +256,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Result<SEBProctoringConnection> getClientRoomConnection(
|
||||||
public Result<SEBProctoringConnection> getClientRoomConnection(
|
|
||||||
final ProctoringSettings proctoringSettings,
|
final ProctoringSettings proctoringSettings,
|
||||||
final String connectionToken,
|
final String connectionToken,
|
||||||
final String roomName,
|
final String roomName,
|
||||||
|
@ -185,8 +286,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected Result<SEBProctoringConnection> createProctoringConnection(
|
||||||
public Result<SEBProctoringConnection> createProctoringConnection(
|
|
||||||
final ProctoringServerType proctoringServerType,
|
final ProctoringServerType proctoringServerType,
|
||||||
final String connectionToken,
|
final String connectionToken,
|
||||||
final String url,
|
final String url,
|
||||||
|
@ -227,35 +327,6 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result<String> createClientAccessToken(
|
|
||||||
final ProctoringSettings proctoringSettings,
|
|
||||||
final String connectionToken,
|
|
||||||
final String roomName) {
|
|
||||||
|
|
||||||
return Result.tryCatch(() -> {
|
|
||||||
|
|
||||||
final ClientConnectionData connectionData = this.examSessionService
|
|
||||||
.getConnectionData(connectionToken)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
final String host = UriComponentsBuilder.fromHttpUrl(proctoringSettings.serverURL)
|
|
||||||
.build()
|
|
||||||
.getHost();
|
|
||||||
final CharSequence decryptedSecret = this.cryptor.decrypt(proctoringSettings.appSecret);
|
|
||||||
|
|
||||||
return internalCreateAccessToken(
|
|
||||||
proctoringSettings.appKey,
|
|
||||||
decryptedSecret,
|
|
||||||
connectionData.clientConnection.userSessionId,
|
|
||||||
"seb-client",
|
|
||||||
roomName,
|
|
||||||
forExam(proctoringSettings),
|
|
||||||
host,
|
|
||||||
false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String internalCreateAccessToken(
|
protected String internalCreateAccessToken(
|
||||||
final String appKey,
|
final String appKey,
|
||||||
final CharSequence appSecret,
|
final CharSequence appSecret,
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -19,12 +18,7 @@ import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.ProctoringInstructionMethod;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
||||||
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;
|
||||||
|
@ -34,7 +28,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
|
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Service
|
@Service
|
||||||
|
@ -45,20 +38,17 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
||||||
|
|
||||||
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
||||||
private final ClientConnectionDAO clientConnectionDAO;
|
private final ClientConnectionDAO clientConnectionDAO;
|
||||||
private final SEBClientInstructionService sebInstructionService;
|
|
||||||
private final ExamAdminService examAdminService;
|
private final ExamAdminService examAdminService;
|
||||||
private final ExamSessionService examSessionService;
|
private final ExamSessionService examSessionService;
|
||||||
|
|
||||||
public ExamProctoringRoomServiceImpl(
|
public ExamProctoringRoomServiceImpl(
|
||||||
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
|
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
|
||||||
final ClientConnectionDAO clientConnectionDAO,
|
final ClientConnectionDAO clientConnectionDAO,
|
||||||
final SEBClientInstructionService sebInstructionService,
|
|
||||||
final ExamAdminService examAdminService,
|
final ExamAdminService examAdminService,
|
||||||
final ExamSessionService examSessionService) {
|
final ExamSessionService examSessionService) {
|
||||||
|
|
||||||
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
||||||
this.clientConnectionDAO = clientConnectionDAO;
|
this.clientConnectionDAO = clientConnectionDAO;
|
||||||
this.sebInstructionService = sebInstructionService;
|
|
||||||
this.examAdminService = examAdminService;
|
this.examAdminService = examAdminService;
|
||||||
this.examSessionService = examSessionService;
|
this.examSessionService = examSessionService;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +74,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
||||||
this.clientConnectionDAO.getAllConnectionIdsForRoomUpdateActive()
|
this.clientConnectionDAO.getAllConnectionIdsForRoomUpdateActive()
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.forEach(this::assignToRoom);
|
.forEach(this::assignToCollectingRoom);
|
||||||
|
|
||||||
this.clientConnectionDAO.getAllConnectionIdsForRoomUpdateInactive()
|
this.clientConnectionDAO.getAllConnectionIdsForRoomUpdateInactive()
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
|
@ -114,40 +104,40 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
||||||
this.remoteProctoringRoomDAO.deleteTownhallRoom(examId);
|
this.remoteProctoringRoomDAO.deleteTownhallRoom(examId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignToRoom(final ClientConnectionRecord cc) {
|
private void assignToCollectingRoom(final ClientConnectionRecord cc) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
final RemoteProctoringRoom proctoringRoom = getProctoringRoom(
|
final RemoteProctoringRoom proctoringRoom = getProctoringRoom(
|
||||||
cc.getExamId(),
|
cc.getExamId(),
|
||||||
cc.getConnectionToken());
|
cc.getConnectionToken());
|
||||||
|
|
||||||
if (proctoringRoom != null) {
|
|
||||||
this.clientConnectionDAO.assignToProctoringRoom(
|
this.clientConnectionDAO.assignToProctoringRoom(
|
||||||
cc.getId(),
|
cc.getId(),
|
||||||
cc.getConnectionToken(),
|
cc.getConnectionToken(),
|
||||||
proctoringRoom.id)
|
proctoringRoom.id)
|
||||||
.onError(error -> log.error("Failed to assign to proctoring room: ", error))
|
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final Result<RemoteProctoringRoom> townhallRoomResult = this.remoteProctoringRoomDAO
|
final Result<RemoteProctoringRoom> townhallRoomResult = this.remoteProctoringRoomDAO
|
||||||
.getTownhallRoom(cc.getExamId());
|
.getTownhallRoom(cc.getExamId());
|
||||||
|
|
||||||
if (townhallRoomResult.hasValue()) {
|
if (townhallRoomResult.hasValue()) {
|
||||||
final RemoteProctoringRoom townhallRoom = townhallRoomResult.get();
|
final RemoteProctoringRoom townhallRoom = townhallRoomResult.get();
|
||||||
applyProcotringInstruction(
|
applyProcotringInstruction(
|
||||||
cc.getExamId(),
|
cc.getExamId(),
|
||||||
cc.getConnectionToken(),
|
cc.getConnectionToken(),
|
||||||
townhallRoom.name,
|
townhallRoom.name,
|
||||||
townhallRoom.subject);
|
townhallRoom.subject)
|
||||||
|
.getOrThrow();
|
||||||
} else {
|
} else {
|
||||||
applyProcotringInstruction(
|
applyProcotringInstruction(
|
||||||
cc.getExamId(),
|
cc.getExamId(),
|
||||||
cc.getConnectionToken(),
|
cc.getConnectionToken(),
|
||||||
proctoringRoom.name,
|
proctoringRoom.name,
|
||||||
proctoringRoom.subject);
|
proctoringRoom.subject)
|
||||||
}
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to update client connection for proctoring room: ", e);
|
log.error("Failed to assign connection to collecting room: {}", cc, e);
|
||||||
this.clientConnectionDAO.setNeedsRoomUpdate(cc.getId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +157,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
||||||
private RemoteProctoringRoom getProctoringRoom(final Long examId, final String connectionToken) {
|
private RemoteProctoringRoom getProctoringRoom(final Long examId, final String connectionToken) {
|
||||||
try {
|
try {
|
||||||
final ProctoringSettings proctoringSettings = this.examAdminService
|
final ProctoringSettings proctoringSettings = this.examAdminService
|
||||||
.getExamProctoring(examId)
|
.getExamProctoringSettings(examId)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
return this.remoteProctoringRoomDAO.reservePlaceInCollectingRoom(
|
return this.remoteProctoringRoomDAO.reservePlaceInCollectingRoom(
|
||||||
examId,
|
examId,
|
||||||
|
@ -184,63 +174,21 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyProcotringInstruction(
|
private Result<Void> applyProcotringInstruction(
|
||||||
final Long examId,
|
final Long examId,
|
||||||
final String connectionToken,
|
final String connectionToken,
|
||||||
final String roomName,
|
final String roomName,
|
||||||
final String subject) {
|
final String subject) {
|
||||||
|
|
||||||
try {
|
return this.examAdminService
|
||||||
// apply a SEB_PROCOTIRNG instruction for the specified SEB client connection
|
.getExamProctoringSettings(examId)
|
||||||
final ProctoringSettings proctoringSettings = this.examAdminService
|
.flatMap(proctoringSettings -> this.examAdminService
|
||||||
.getExamProctoring(examId)
|
.getExamProctoringService(proctoringSettings.serverType)
|
||||||
.getOrThrow();
|
.getOrThrow()
|
||||||
|
.sendJoinCollectingRoomToClients(
|
||||||
final SEBProctoringConnection proctoringData =
|
|
||||||
this.examAdminService.getExamProctoringService(proctoringSettings.serverType)
|
|
||||||
.flatMap(s -> s.getClientExamCollectingRoomConnection(
|
|
||||||
proctoringSettings,
|
proctoringSettings,
|
||||||
connectionToken,
|
Arrays.asList(connectionToken)));
|
||||||
roomName,
|
|
||||||
subject))
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
final Map<String, String> attributes = new HashMap<>();
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.SERVICE_TYPE,
|
|
||||||
ProctoringServerType.JITSI_MEET.name());
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.METHOD,
|
|
||||||
ProctoringInstructionMethod.JOIN.name());
|
|
||||||
|
|
||||||
if (proctoringSettings.serverType == ProctoringServerType.JITSI_MEET) {
|
|
||||||
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_ROOM,
|
|
||||||
proctoringData.roomName);
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_ROOM_SUBJECT,
|
|
||||||
proctoringData.subject);
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_URL,
|
|
||||||
proctoringData.serverURL);
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_TOKEN,
|
|
||||||
proctoringData.accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sebInstructionService.registerInstruction(
|
|
||||||
examId,
|
|
||||||
InstructionType.SEB_PROCTORING,
|
|
||||||
attributes,
|
|
||||||
connectionToken,
|
|
||||||
true);
|
|
||||||
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.error(
|
|
||||||
"Failed to process proctoring initialization for established SEB client connection: {}",
|
|
||||||
connectionToken, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -379,6 +379,12 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Collection<String>> getActiveConnectionTokens(final Long examId) {
|
||||||
|
return this.clientConnectionDAO
|
||||||
|
.getActiveConnctionTokens(examId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Exam> updateExamCache(final Long examId) {
|
public Result<Exam> updateExamCache(final Long examId) {
|
||||||
final Exam exam = this.examSessionCacheService.getRunningExam(examId);
|
final Exam exam = this.examSessionCacheService.getRunningExam(examId);
|
||||||
|
|
|
@ -396,7 +396,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
||||||
checkReadPrivilege(institutionId);
|
checkReadPrivilege(institutionId);
|
||||||
return this.entityDAO.byPK(modelId)
|
return this.entityDAO.byPK(modelId)
|
||||||
.flatMap(this.authorization::checkRead)
|
.flatMap(this.authorization::checkRead)
|
||||||
.flatMap(this.examAdminService::getExamProctoring)
|
.flatMap(this.examAdminService::getExamProctoringSettings)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,7 +417,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
||||||
return this.entityDAO.byPK(examId)
|
return this.entityDAO.byPK(examId)
|
||||||
.flatMap(this.authorization::checkModify)
|
.flatMap(this.authorization::checkModify)
|
||||||
.map(exam -> {
|
.map(exam -> {
|
||||||
this.examAdminService.saveExamProctoring(examId, examProctoring);
|
this.examAdminService.saveExamProctoringSettings(examId, examProctoring);
|
||||||
return exam;
|
return exam;
|
||||||
})
|
})
|
||||||
.flatMap(this.userActivityLogDAO::logModify)
|
.flatMap(this.userActivityLogDAO::logModify)
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.Base64.Encoder;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -39,8 +37,6 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
|
||||||
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.Utils;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||||
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.exam.ExamAdminService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
||||||
|
@ -112,7 +108,7 @@ public class ExamProctoringController {
|
||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public SEBProctoringConnection getProctorRoomData(
|
public SEBProctoringConnection getProctorRoomConnection(
|
||||||
@RequestParam(
|
@RequestParam(
|
||||||
name = API.PARAM_INSTITUTION_ID,
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
required = true,
|
required = true,
|
||||||
|
@ -125,13 +121,11 @@ public class ExamProctoringController {
|
||||||
|
|
||||||
return this.examSessionService.getRunningExam(examId)
|
return this.examSessionService.getRunningExam(examId)
|
||||||
.flatMap(this.authorization::checkRead)
|
.flatMap(this.authorization::checkRead)
|
||||||
.flatMap(this.examAdminService::getExamProctoring)
|
.flatMap(this.examAdminService::getExamProctoringService)
|
||||||
.flatMap(proc -> this.examAdminService
|
.flatMap(service -> service.getProctorRoomConnection(
|
||||||
.getExamProctoringService(proc.serverType)
|
this.examAdminService.getExamProctoringSettings(examId).getOrThrow(),
|
||||||
.flatMap(s -> s.createProctorPublicRoomConnection(
|
|
||||||
proc,
|
|
||||||
roomName,
|
roomName,
|
||||||
StringUtils.isNoneBlank(subject) ? subject : roomName)))
|
StringUtils.isNoneBlank(subject) ? subject : roomName))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,10 +217,17 @@ public class ExamProctoringController {
|
||||||
|
|
||||||
final ProctoringSettings proctoringSettings = this.examSessionService
|
final ProctoringSettings proctoringSettings = this.examSessionService
|
||||||
.getRunningExam(examId)
|
.getRunningExam(examId)
|
||||||
.flatMap(this.examAdminService::getExamProctoring)
|
.flatMap(this.examAdminService::getExamProctoringSettings)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
sendJoinInstructions(connectionTokens, proctoringSettings);
|
this.examAdminService
|
||||||
|
.getExamProctoringService(proctoringSettings.serverType)
|
||||||
|
.flatMap(service -> service.sendJoinCollectingRoomToClients(
|
||||||
|
proctoringSettings,
|
||||||
|
Arrays.asList(StringUtils.split(
|
||||||
|
connectionTokens,
|
||||||
|
Constants.LIST_SEPARATOR_CHAR))))
|
||||||
|
.onError(error -> log.error("Failed to send rejoin collecting room to: {}", connectionTokens, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
|
@ -254,57 +255,21 @@ public class ExamProctoringController {
|
||||||
|
|
||||||
final ProctoringSettings settings = this.examSessionService
|
final ProctoringSettings settings = this.examSessionService
|
||||||
.getRunningExam(examId)
|
.getRunningExam(examId)
|
||||||
.flatMap(this.examAdminService::getExamProctoring)
|
.flatMap(this.examAdminService::getExamProctoringSettings)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final ExamProctoringService examProctoringService = this.examAdminService
|
return this.examAdminService
|
||||||
.getExamProctoringService(settings.serverType)
|
.getExamProctoringService(settings.serverType)
|
||||||
.getOrThrow();
|
.flatMap(service -> service.sendJoinRoomToClients(
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(connectionTokens)) {
|
|
||||||
|
|
||||||
Arrays.asList(connectionTokens.split(Constants.LIST_SEPARATOR))
|
|
||||||
.stream()
|
|
||||||
.forEach(connectionToken -> {
|
|
||||||
final SEBProctoringConnection proctoringConnection =
|
|
||||||
examProctoringService
|
|
||||||
.getClientRoomConnection(
|
|
||||||
settings,
|
|
||||||
connectionToken,
|
|
||||||
verifyRoomName(roomName, connectionToken),
|
|
||||||
(StringUtils.isNotBlank(subject)) ? subject : roomName)
|
|
||||||
.onError(error -> log.error(
|
|
||||||
"Failed to get client room connection data for {} cause: {}",
|
|
||||||
connectionToken,
|
|
||||||
error.getMessage()))
|
|
||||||
.get();
|
|
||||||
if (proctoringConnection != null) {
|
|
||||||
sendJoinInstruction(settings.examId, connectionToken, proctoringConnection)
|
|
||||||
.onError(error -> log.error(
|
|
||||||
"Failed to send proctoring leave instruction to client: {} cause: {}",
|
|
||||||
connectionToken,
|
|
||||||
error.getMessage()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return examProctoringService.createProctorPublicRoomConnection(
|
|
||||||
settings,
|
settings,
|
||||||
|
Arrays.asList(StringUtils.split(
|
||||||
|
connectionTokens,
|
||||||
|
Constants.LIST_SEPARATOR_CHAR)),
|
||||||
roomName,
|
roomName,
|
||||||
(StringUtils.isNotBlank(subject)) ? subject : roomName)
|
subject))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String verifyRoomName(final String requestedRoomName, final String connectionToken) {
|
|
||||||
if (StringUtils.isNotBlank(requestedRoomName)) {
|
|
||||||
return requestedRoomName;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
|
||||||
return urlEncoder.encodeToString(
|
|
||||||
Utils.toByteArray(connectionToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||||
+ API.EXAM_PROCTORING_TOWNHALL_ROOM_DATA,
|
+ API.EXAM_PROCTORING_TOWNHALL_ROOM_DATA,
|
||||||
|
@ -342,48 +307,27 @@ public class ExamProctoringController {
|
||||||
|
|
||||||
final ProctoringSettings settings = this.examSessionService
|
final ProctoringSettings settings = this.examSessionService
|
||||||
.getRunningExam(examId)
|
.getRunningExam(examId)
|
||||||
.flatMap(this.examAdminService::getExamProctoring)
|
.flatMap(this.examAdminService::getExamProctoringSettings)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final ExamProctoringService examProctoringService = this.examAdminService
|
// First create and get the town-hall room for specified exam
|
||||||
|
final RemoteProctoringRoom townhallRoom = this.examProcotringRoomService
|
||||||
|
.createTownhallRoom(examId, subject)
|
||||||
|
.onError(error -> {
|
||||||
|
log.error("Failed to create town-hall room: ", error);
|
||||||
|
this.examProcotringRoomService.disposeTownhallRoom(examId);
|
||||||
|
})
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
// Then send a join instruction to all active clients of the exam to join the town-hall
|
||||||
|
return this.examAdminService
|
||||||
.getExamProctoringService(settings.serverType)
|
.getExamProctoringService(settings.serverType)
|
||||||
.getOrThrow();
|
.flatMap(service -> service.sendJoinRoomToClients(
|
||||||
|
|
||||||
// first create and register a room to collect all connection of the exam
|
|
||||||
// As long as the room exists new connections will join this room immediately
|
|
||||||
// after have been applied to the default collecting room
|
|
||||||
final RemoteProctoringRoom townhallRoom = this.examProcotringRoomService.createTownhallRoom(examId, subject)
|
|
||||||
.onError(error -> this.examProcotringRoomService.disposeTownhallRoom(examId))
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
// get all active connections for the exam and send the join instruction
|
|
||||||
this.examSessionService.getAllActiveConnectionData(examId)
|
|
||||||
.getOrThrow()
|
|
||||||
.stream()
|
|
||||||
.forEach(cc -> {
|
|
||||||
final SEBProctoringConnection data = examProctoringService
|
|
||||||
.getClientRoomConnection(
|
|
||||||
settings,
|
settings,
|
||||||
cc.clientConnection.connectionToken,
|
this.examSessionService.getActiveConnectionTokens(examId)
|
||||||
|
.getOrThrow(),
|
||||||
townhallRoom.name,
|
townhallRoom.name,
|
||||||
townhallRoom.subject)
|
townhallRoom.subject))
|
||||||
.onError(error -> log.error(
|
|
||||||
"Failed to get client room connection data for {} cause: {}",
|
|
||||||
cc.clientConnection.connectionToken,
|
|
||||||
error.getMessage()))
|
|
||||||
.get();
|
|
||||||
if (data != null) {
|
|
||||||
sendJoinInstruction(examId, cc.clientConnection.connectionToken, data)
|
|
||||||
.onError(error -> log.error(
|
|
||||||
"Failed to send proctoring leave instruction to client: {} ",
|
|
||||||
cc.clientConnection.connectionToken, error));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return examProctoringService.createProctorPublicRoomConnection(
|
|
||||||
settings,
|
|
||||||
townhallRoom.name,
|
|
||||||
townhallRoom.subject)
|
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +347,7 @@ public class ExamProctoringController {
|
||||||
|
|
||||||
final ProctoringSettings settings = this.examSessionService
|
final ProctoringSettings settings = this.examSessionService
|
||||||
.getRunningExam(examId)
|
.getRunningExam(examId)
|
||||||
.flatMap(this.examAdminService::getExamProctoring)
|
.flatMap(this.examAdminService::getExamProctoringSettings)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final ExamProctoringService examProctoringService = this.examAdminService
|
final ExamProctoringService examProctoringService = this.examAdminService
|
||||||
|
@ -413,25 +357,11 @@ public class ExamProctoringController {
|
||||||
// first unregister the current room to collect all connection of the exam
|
// first unregister the current room to collect all connection of the exam
|
||||||
this.examProcotringRoomService.disposeTownhallRoom(examId);
|
this.examProcotringRoomService.disposeTownhallRoom(examId);
|
||||||
|
|
||||||
// get all active connections for the exam and send the join instruction
|
// then get all active connections for the exam and send the rejoin to collecting room instruction
|
||||||
this.examSessionService.getConnectionData(
|
examProctoringService.sendJoinCollectingRoomToClients(
|
||||||
examId,
|
|
||||||
ExamSessionService.ACTIVE_CONNECTION_DATA_FILTER)
|
|
||||||
.getOrThrow()
|
|
||||||
.stream()
|
|
||||||
.forEach(cc -> {
|
|
||||||
examProctoringService
|
|
||||||
.getClientExamCollectingRoomConnection(
|
|
||||||
settings,
|
settings,
|
||||||
cc.clientConnection)
|
this.examSessionService.getActiveConnectionTokens(examId)
|
||||||
.flatMap(data -> this.sendJoinInstruction(
|
.getOrThrow());
|
||||||
examId,
|
|
||||||
cc.clientConnection.connectionToken,
|
|
||||||
data))
|
|
||||||
.onError(error -> log.error("Failed to send rejoin for: {} cause: {}",
|
|
||||||
cc.clientConnection.connectionToken,
|
|
||||||
error.getMessage()));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendBroadcastInstructions(
|
private void sendBroadcastInstructions(
|
||||||
|
@ -486,19 +416,19 @@ public class ExamProctoringController {
|
||||||
|
|
||||||
private void sendBroadcastInstructionToClientsInExam(final Long examId, final Map<String, String> attributes) {
|
private void sendBroadcastInstructionToClientsInExam(final Long examId, final Map<String, String> attributes) {
|
||||||
this.examSessionService
|
this.examSessionService
|
||||||
.getAllActiveConnectionData(examId)
|
.getActiveConnectionTokens(examId)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.forEach(connection -> {
|
.forEach(connectionToken -> {
|
||||||
this.sebInstructionService.registerInstruction(
|
this.sebInstructionService.registerInstruction(
|
||||||
examId,
|
examId,
|
||||||
InstructionType.SEB_RECONFIGURE_SETTINGS,
|
InstructionType.SEB_RECONFIGURE_SETTINGS,
|
||||||
attributes,
|
attributes,
|
||||||
connection.clientConnection.connectionToken,
|
connectionToken,
|
||||||
true)
|
true)
|
||||||
.onError(error -> log.error(
|
.onError(error -> log.error(
|
||||||
"Failed to register reconfiguring instruction for connection: {}",
|
"Failed to register reconfiguring instruction for connection: {}",
|
||||||
connection.clientConnection.connectionToken,
|
connectionToken,
|
||||||
error));
|
error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -527,70 +457,6 @@ public class ExamProctoringController {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendJoinInstructions(
|
|
||||||
final String connectionTokens,
|
|
||||||
final ProctoringSettings proctoringSettings) {
|
|
||||||
|
|
||||||
final ExamProctoringService examProctoringService = this.examAdminService
|
|
||||||
.getExamProctoringService(proctoringSettings.serverType)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
|
|
||||||
.stream()
|
|
||||||
.forEach(connectionToken -> {
|
|
||||||
sendJoinInstructionToClient(proctoringSettings, examProctoringService, connectionToken);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendJoinInstructionToClient(
|
|
||||||
final ProctoringSettings proctoringSettings,
|
|
||||||
final ExamProctoringService examProctoringService,
|
|
||||||
final String connectionToken) {
|
|
||||||
|
|
||||||
this.examSessionService
|
|
||||||
.getConnectionData(connectionToken)
|
|
||||||
.flatMap(connection -> examProctoringService.getClientExamCollectingRoomConnection(
|
|
||||||
proctoringSettings,
|
|
||||||
connection.clientConnection))
|
|
||||||
.flatMap(data -> this.sendJoinInstruction(
|
|
||||||
proctoringSettings.examId,
|
|
||||||
connectionToken, data))
|
|
||||||
.onError(error -> log.error("Failed to send rejoin for: {} cause: {}",
|
|
||||||
connectionToken,
|
|
||||||
error.getMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result<Void> sendJoinInstruction(
|
|
||||||
final Long examId,
|
|
||||||
final String connectionToken,
|
|
||||||
final SEBProctoringConnection data) {
|
|
||||||
|
|
||||||
final Map<String, String> attributes = new HashMap<>();
|
|
||||||
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.SERVICE_TYPE,
|
|
||||||
ProctoringSettings.ProctoringServerType.JITSI_MEET.name());
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.METHOD,
|
|
||||||
ClientInstruction.ProctoringInstructionMethod.JOIN.name());
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_URL,
|
|
||||||
data.serverURL);
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_ROOM,
|
|
||||||
data.roomName);
|
|
||||||
attributes.put(
|
|
||||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_TOKEN,
|
|
||||||
data.accessToken);
|
|
||||||
|
|
||||||
return this.sebInstructionService.registerInstruction(
|
|
||||||
examId,
|
|
||||||
InstructionType.SEB_PROCTORING,
|
|
||||||
attributes,
|
|
||||||
connectionToken,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAccess(final Long institutionId, final Long examId) {
|
private void checkAccess(final Long institutionId, final Long examId) {
|
||||||
this.authorization.check(
|
this.authorization.check(
|
||||||
PrivilegeType.READ,
|
PrivilegeType.READ,
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class ExamJITSIProctoringServiceTest {
|
||||||
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
||||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
||||||
final ExamJITSIProctoringService examJITSIProctoringService =
|
final ExamJITSIProctoringService examJITSIProctoringService =
|
||||||
new ExamJITSIProctoringService(null, null, null, cryptorMock);
|
new ExamJITSIProctoringService(null, null, null, null, cryptorMock);
|
||||||
|
|
||||||
String accessToken = examJITSIProctoringService.createPayload(
|
String accessToken = examJITSIProctoringService.createPayload(
|
||||||
"test-app",
|
"test-app",
|
||||||
|
@ -62,7 +62,7 @@ public class ExamJITSIProctoringServiceTest {
|
||||||
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
||||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
||||||
final ExamJITSIProctoringService examJITSIProctoringService =
|
final ExamJITSIProctoringService examJITSIProctoringService =
|
||||||
new ExamJITSIProctoringService(null, null, null, cryptorMock);
|
new ExamJITSIProctoringService(null, null, null, null, cryptorMock);
|
||||||
final SEBProctoringConnection data = examJITSIProctoringService.createProctoringConnection(
|
final SEBProctoringConnection data = examJITSIProctoringService.createProctoringConnection(
|
||||||
ProctoringServerType.JITSI_MEET,
|
ProctoringServerType.JITSI_MEET,
|
||||||
"connectionToken",
|
"connectionToken",
|
||||||
|
|
Loading…
Reference in a new issue