diff --git a/pom.xml b/pom.xml
index d7dcf41d..c670a1ed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
jar
- 1.1.0-rc1
+ 1.2.0-SNAPSHOT
${sebserver-version}
${sebserver-version}
UTF-8
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/SEBProctoringConnectionData.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/SEBProctoringConnection.java
similarity index 94%
rename from src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/SEBProctoringConnectionData.java
rename to src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/SEBProctoringConnection.java
index 1f6cd88a..f28cc761 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/SEBProctoringConnectionData.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/SEBProctoringConnection.java
@@ -15,7 +15,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
@JsonIgnoreProperties(ignoreUnknown = true)
-public class SEBProctoringConnectionData {
+public class SEBProctoringConnection {
public static final String ATTR_CONNECTION_TOKEN = "connectionToken";
public static final String ATTR_SERVER_HOST = "serverHost";
@@ -47,7 +47,7 @@ public class SEBProctoringConnectionData {
public final String accessToken;
@JsonCreator
- public SEBProctoringConnectionData(
+ public SEBProctoringConnection(
@JsonProperty(ProctoringSettings.ATTR_SERVER_TYPE) final ProctoringServerType proctoringServerType,
@JsonProperty(ATTR_CONNECTION_TOKEN) final String connectionToken,
@JsonProperty(ATTR_SERVER_HOST) final String serverHost,
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java
index 0721d85f..8a927f1d 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringClientConnection.java
@@ -32,7 +32,7 @@ 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.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
@@ -455,12 +455,12 @@ public class MonitoringClientConnection implements TemplateComposer {
if (roomOptional.isPresent()) {
final RemoteProctoringRoom room = roomOptional.get();
- final SEBProctoringConnectionData proctoringConnectionData = this.pageService
+ final SEBProctoringConnection proctoringConnectionData = this.pageService
.getRestService()
.getBuilder(GetProctorRoomConnectionData.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(proctoringSettings.examId))
- .withQueryParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, room.name)
- .withQueryParam(SEBProctoringConnectionData.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
+ .withQueryParam(SEBProctoringConnection.ATTR_ROOM_NAME, room.name)
+ .withQueryParam(SEBProctoringConnection.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
.call()
.getOrThrow();
@@ -499,7 +499,7 @@ public class MonitoringClientConnection implements TemplateComposer {
.getProctoringGUIService();
if (!proctoringGUIService.hasRoom(roomName)) {
- final SEBProctoringConnectionData proctoringConnectionData = proctoringGUIService
+ final SEBProctoringConnection proctoringConnectionData = proctoringGUIService
.registerNewSingleProcotringRoom(
examId,
roomName,
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java
index 9d81d9d9..31384816 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/MonitoringRunningExam.java
@@ -44,7 +44,7 @@ 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.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+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.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
@@ -445,7 +445,7 @@ public class MonitoringRunningExam implements TemplateComposer {
String activeAllRoomName = proctoringGUIService.getTownhallRoom(examId.modelId);
if (activeAllRoomName == null) {
- final SEBProctoringConnectionData proctoringConnectionData = proctoringGUIService
+ final SEBProctoringConnection proctoringConnectionData = proctoringGUIService
.registerTownhallRoom(
examId.modelId,
this.pageService.getI18nSupport().getText(EXAM_ROOM_NAME))
@@ -642,12 +642,12 @@ public class MonitoringRunningExam implements TemplateComposer {
final RemoteProctoringRoom room,
final PageAction action) {
- final SEBProctoringConnectionData proctoringConnectionData = this.pageService
+ final SEBProctoringConnection proctoringConnectionData = this.pageService
.getRestService()
.getBuilder(GetProctorRoomConnectionData.class)
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(proctoringSettings.examId))
- .withQueryParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, room.name)
- .withQueryParam(SEBProctoringConnectionData.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
+ .withQueryParam(SEBProctoringConnection.ATTR_ROOM_NAME, room.name)
+ .withQueryParam(SEBProctoringConnection.ATTR_SUBJECT, Utils.encodeFormURL_UTF_8(room.subject))
.call()
.getOrThrow();
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizLookupList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizLookupList.java
index a6ae673e..a60278ae 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizLookupList.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/QuizLookupList.java
@@ -115,9 +115,11 @@ public class QuizLookupList implements TemplateComposer {
private final ResourceService resourceService;
private final PageService pageService;
private final int pageSize;
+ private final DateTime filterStartDate;
protected QuizLookupList(
final PageService pageService,
+ @Value("${sebserver.gui.filter.date.from.years:2}") final Integer startYearFromNow,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
this.pageService = pageService;
@@ -125,6 +127,10 @@ public class QuizLookupList implements TemplateComposer {
this.resourceService = pageService.getResourceService();
this.pageSize = pageSize;
+ this.filterStartDate = Utils
+ .toDateTimeUTC(Utils.getMillisecondsNow())
+ .minusYears(startYearFromNow);
+
this.institutionFilter = new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Entity.FILTER_ATTR_INSTITUTION,
@@ -194,9 +200,7 @@ public class QuizLookupList implements TemplateComposer {
.withFilter(new TableFilterAttribute(
CriteriaType.DATE,
QuizData.FILTER_ATTR_START_TIME,
- Utils.toDateTimeUTC(Utils.getMillisecondsNow())
- .minusYears(1)
- .toString()))
+ this.filterStartDate.toString()))
.sortable())
.withColumn(new ColumnDefinition<>(
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigForm.java
index 8dcdf923..7cdeaaad 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigForm.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigForm.java
@@ -245,13 +245,6 @@ public class SEBExamConfigForm implements TemplateComposer {
.noEventPropagation()
.publishIf(() -> modifyGrant && isReadonly)
-// // TODO shall this got to settings form?
-// .newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG)
-// .withEntityKey(entityKey)
-// .withExec(this.sebExamConfigImportPopup.importFunction(false))
-// .noEventPropagation()
-// .publishIf(() -> modifyGrant && isReadonly && !isAttachedToExam)
-
.newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_SAVE)
.withEntityKey(entityKey)
.withExec(formHandle::processFormSave)
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigImportPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigImportPopup.java
index a09c1bc7..1b52f458 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigImportPopup.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBExamConfigImportPopup.java
@@ -104,133 +104,92 @@ public class SEBExamConfigImportPopup {
final Control fieldControl = form.getFieldInput(API.IMPORT_FILE_ATTR_NAME);
final PageContext context = formHandle.getContext();
- // Ad-hoc field validation
- if (newConfig) {
- formHandle.process(name -> true, Form.FormFieldAccessor::resetError);
- final String fieldValue = form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME);
- if (StringUtils.isBlank(fieldValue)) {
- form.setFieldError(
- Domain.CONFIGURATION_NODE.ATTR_NAME,
- this.pageService
- .getI18nSupport()
- .getText(new LocTextKey("sebserver.form.validation.fieldError.notNull")));
- return false;
- } else if (fieldValue.length() < 3 || fieldValue.length() > 255) {
- form.setFieldError(
- Domain.CONFIGURATION_NODE.ATTR_NAME,
- this.pageService
- .getI18nSupport()
- .getText(new LocTextKey("sebserver.form.validation.fieldError.size",
- null,
- null,
- null,
- 3,
- 255)));
- return false;
- } else {
- // check if name already exists
- try {
- if (this.pageService.getRestService()
- .getBuilder(GetExamConfigNodeNames.class)
- .call()
- .getOrThrow()
- .stream()
- .filter(n -> n.name.equals(fieldValue))
- .findFirst()
- .isPresent()) {
-
- form.setFieldError(
- Domain.CONFIGURATION_NODE.ATTR_NAME,
- this.pageService
- .getI18nSupport()
- .getText(new LocTextKey(
- "sebserver.form.validation.fieldError.name.notunique")));
- return false;
- }
- } catch (final Exception e) {
- log.error("Failed to verify unique name: {}", e.getMessage());
- }
- }
+ if (!(fieldControl instanceof FileUploadSelection)) {
+ return false;
}
- if (fieldControl instanceof FileUploadSelection) {
- final FileUploadSelection fileUpload = (FileUploadSelection) fieldControl;
- final InputStream inputStream = fileUpload.getInputStream();
- if (inputStream != null) {
- final RestCall.RestCallBuilder restCall = (newConfig)
- ? this.pageService.getRestService()
- .getBuilder(ImportNewExamConfig.class)
- : this.pageService.getRestService()
- .getBuilder(ImportExamConfigOnExistingConfig.class);
+ if (!checkInput(formHandle, newConfig, form)) {
+ return false;
+ }
+ final FileUploadSelection fileUpload = (FileUploadSelection) fieldControl;
+ final InputStream inputStream = fileUpload.getInputStream();
+ if (inputStream != null) {
+ final RestCall.RestCallBuilder restCall = (newConfig)
+ ? this.pageService.getRestService()
+ .getBuilder(ImportNewExamConfig.class)
+ : this.pageService.getRestService()
+ .getBuilder(ImportExamConfigOnExistingConfig.class);
+
+ restCall
+ .withHeader(
+ API.IMPORT_PASSWORD_ATTR_NAME,
+ form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
+ .withBody(inputStream);
+
+ if (newConfig) {
restCall
.withHeader(
- API.IMPORT_PASSWORD_ATTR_NAME,
- form.getFieldValue(API.IMPORT_PASSWORD_ATTR_NAME))
- .withBody(inputStream);
+ Domain.CONFIGURATION_NODE.ATTR_NAME,
+ form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME))
+ .withHeader(
+ Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
+ form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION))
+ .withHeader(
+ Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID,
+ form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID));
+ } else {
+ restCall.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId);
+ }
- if (newConfig) {
- restCall
- .withHeader(
- Domain.CONFIGURATION_NODE.ATTR_NAME,
- form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME))
- .withHeader(
- Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION,
- form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_DESCRIPTION))
- .withHeader(
- Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID,
- form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID));
- } else {
- restCall.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId);
- }
+ final Result configuration = restCall
+ .call();
- final Result configuration = restCall
- .call();
-
- if (!configuration.hasError()) {
- context.publishInfo(SEBExamConfigForm.FORM_IMPORT_CONFIRM_TEXT_KEY);
- if (newConfig) {
-
- final PageAction action = this.pageService.pageActionBuilder(context)
+ if (!configuration.hasError()) {
+ context.publishInfo(SEBExamConfigForm.FORM_IMPORT_CONFIRM_TEXT_KEY);
+ final PageAction action = (newConfig)
+ ? this.pageService.pageActionBuilder(context)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_NEW_CONFIG)
+ .create()
+ : this.pageService.pageActionBuilder(context)
+ .newAction(ActionDefinition.SEB_EXAM_CONFIG_MODIFY)
.create();
- this.pageService.firePageEvent(
- new ActionEvent(action),
- action.pageContext());
- }
- } else {
- final Exception error = configuration.getError();
- if (error instanceof RestCallError) {
- ((RestCallError) error)
- .getErrorMessages()
- .stream()
- .findFirst()
- .ifPresent(message -> {
- if (APIMessage.ErrorMessage.MISSING_PASSWORD.isOf(message)) {
- formHandle
- .getContext()
- .publishPageMessage(MISSING_PASSWORD);
- } else {
- formHandle
- .getContext()
- .notifyImportError(EntityType.CONFIGURATION_NODE, error);
- }
- });
- return true;
- }
+ this.pageService.firePageEvent(
+ new ActionEvent(action),
+ action.pageContext());
- formHandle.getContext().notifyError(
- SEBExamConfigForm.FORM_TITLE,
- configuration.getError());
-
- }
- return true;
} else {
- formHandle.getContext().publishPageMessage(
- new LocTextKey("sebserver.error.unexpected"),
- new LocTextKey("Please select a valid SEB Exam Configuration File"));
+ final Exception error = configuration.getError();
+ if (error instanceof RestCallError) {
+ ((RestCallError) error)
+ .getErrorMessages()
+ .stream()
+ .findFirst()
+ .ifPresent(message -> {
+ if (APIMessage.ErrorMessage.MISSING_PASSWORD.isOf(message)) {
+ formHandle
+ .getContext()
+ .publishPageMessage(MISSING_PASSWORD);
+ } else {
+ formHandle
+ .getContext()
+ .notifyImportError(EntityType.CONFIGURATION_NODE, error);
+ }
+ });
+ return true;
+ }
+
+ formHandle.getContext().notifyError(
+ SEBExamConfigForm.FORM_TITLE,
+ configuration.getError());
+
}
+ return true;
+ } else {
+ formHandle.getContext().publishPageMessage(
+ new LocTextKey("sebserver.error.unexpected"),
+ new LocTextKey("Please select a valid SEB Exam Configuration File"));
}
return false;
@@ -240,6 +199,58 @@ public class SEBExamConfigImportPopup {
}
}
+ private boolean checkInput(final FormHandle formHandle, final boolean newConfig,
+ final Form form) {
+ if (newConfig) {
+ formHandle.process(name -> true, Form.FormFieldAccessor::resetError);
+ final String fieldValue = form.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_NAME);
+ if (StringUtils.isBlank(fieldValue)) {
+ form.setFieldError(
+ Domain.CONFIGURATION_NODE.ATTR_NAME,
+ this.pageService
+ .getI18nSupport()
+ .getText(new LocTextKey("sebserver.form.validation.fieldError.notNull")));
+ return false;
+ } else if (fieldValue.length() < 3 || fieldValue.length() > 255) {
+ form.setFieldError(
+ Domain.CONFIGURATION_NODE.ATTR_NAME,
+ this.pageService
+ .getI18nSupport()
+ .getText(new LocTextKey("sebserver.form.validation.fieldError.size",
+ null,
+ null,
+ null,
+ 3,
+ 255)));
+ return false;
+ } else {
+ // check if name already exists
+ try {
+ if (this.pageService.getRestService()
+ .getBuilder(GetExamConfigNodeNames.class)
+ .call()
+ .getOrThrow()
+ .stream()
+ .filter(n -> n.name.equals(fieldValue))
+ .findFirst()
+ .isPresent()) {
+
+ form.setFieldError(
+ Domain.CONFIGURATION_NODE.ATTR_NAME,
+ this.pageService
+ .getI18nSupport()
+ .getText(new LocTextKey(
+ "sebserver.form.validation.fieldError.name.notunique")));
+ return false;
+ }
+ } catch (final Exception e) {
+ log.error("Failed to verify unique name: {}", e.getMessage());
+ }
+ }
+ }
+ return true;
+ }
+
private final class ImportFormContext implements ModalInputDialogComposer> {
private final PageService pageService;
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBSettingsForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBSettingsForm.java
index 6070ea7f..f4c2f96f 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBSettingsForm.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/SEBSettingsForm.java
@@ -255,7 +255,6 @@ public class SEBSettingsForm implements TemplateComposer {
.newAction(ActionDefinition.SEB_EXAM_CONFIG_IMPORT_TO_EXISTING_CONFIG)
.withEntityKey(entityKey)
.withExec(this.sebExamConfigImportPopup.importFunction(false))
- .noEventPropagation()
.publishIf(() -> examConfigGrant.iw() && !readonly && !isAttachedToExam)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_VIEW_PROP)
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/BrowserViewModeRule.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/BrowserViewModeRule.java
index 9f8d3e5c..8d7686f5 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/BrowserViewModeRule.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/BrowserViewModeRule.java
@@ -56,7 +56,9 @@ public class BrowserViewModeRule implements ValueChangeRule {
if (KEY_TOUCH_OPTIMIZED.equals(attribute.name)) {
if (BooleanUtils.toBoolean(value.value)) {
context.disableGroup(KEY_MAIN_WINDOW_GROUP);
- context.setValue(KEY_BROWSER_VIEW_MODE, "2");
+ context.setValue(
+ KEY_BROWSER_VIEW_MODE,
+ context.getAttributeByName(KEY_BROWSER_VIEW_MODE).defaultValue);
} else {
context.setValue(KEY_TOUCH_EXIT, Constants.FALSE_STRING);
context.disable(KEY_TOUCH_EXIT);
@@ -70,7 +72,9 @@ public class BrowserViewModeRule implements ValueChangeRule {
case 1: {
context.disable(KEY_TOUCH_EXIT);
context.disableGroup(KEY_MAIN_WINDOW_GROUP);
- context.setValue(KEY_TOUCH_OPTIMIZED, Constants.FALSE_STRING);
+ context.setValue(
+ KEY_TOUCH_OPTIMIZED,
+ context.getAttributeByName(KEY_TOUCH_OPTIMIZED).defaultValue);
break;
}
case 2: {
@@ -79,7 +83,9 @@ public class BrowserViewModeRule implements ValueChangeRule {
}
default: {
context.disable(KEY_TOUCH_EXIT);
- context.setValue(KEY_TOUCH_OPTIMIZED, Constants.FALSE_STRING);
+ context.setValue(
+ KEY_TOUCH_OPTIMIZED,
+ context.getAttributeByName(KEY_TOUCH_OPTIMIZED).defaultValue);
break;
}
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/IgnoreSEBService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/IgnoreSEBService.java
new file mode 100644
index 00000000..e28f0b8e
--- /dev/null
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/rules/IgnoreSEBService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * 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.gui.service.examconfig.impl.rules;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
+import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
+import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
+import ch.ethz.seb.sebserver.gui.service.examconfig.ValueChangeRule;
+import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
+
+@Lazy
+@Service
+@GuiProfile
+public class IgnoreSEBService implements ValueChangeRule {
+
+ public static final String KEY_IGNORE_SEB_SERVICE = "sebServiceIgnore";
+
+ public static final String KEY_SEB_SERVICE_POLICY = "sebServicePolicy";
+ public static final String KEY_ATTR_1 = "enableWindowsUpdate";
+ public static final String KEY_ATTR_2 = "enableChromeNotifications";
+ public static final String KEY_ATTR_3 = "allowScreenSharing";
+
+ @Override
+ public boolean observesAttribute(final ConfigurationAttribute attribute) {
+ return KEY_IGNORE_SEB_SERVICE.equals(attribute.name);
+ }
+
+ @Override
+ public void applyRule(
+ final ViewContext context,
+ final ConfigurationAttribute attribute,
+ final ConfigurationValue value) {
+
+ if (KEY_IGNORE_SEB_SERVICE.equals(attribute.name)) {
+ if (BooleanUtils.toBoolean(value.value)) {
+ context.disable(KEY_SEB_SERVICE_POLICY);
+ context.disable(KEY_ATTR_1);
+ context.disable(KEY_ATTR_2);
+ context.disable(KEY_ATTR_3);
+
+ context.setValue(
+ KEY_SEB_SERVICE_POLICY,
+ context.getAttributeByName(KEY_SEB_SERVICE_POLICY).defaultValue);
+ context.setValue(
+ KEY_ATTR_1,
+ context.getAttributeByName(KEY_ATTR_1).defaultValue);
+ context.setValue(
+ KEY_ATTR_2,
+ context.getAttributeByName(KEY_ATTR_2).defaultValue);
+ context.setValue(
+ KEY_ATTR_3,
+ context.getAttributeByName(KEY_ATTR_3).defaultValue);
+ } else {
+ context.enable(KEY_SEB_SERVICE_POLICY);
+ context.enable(KEY_ATTR_1);
+ context.enable(KEY_ATTR_2);
+ context.enable(KEY_ATTR_3);
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/ActivateTownhallRoom.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/ActivateTownhallRoom.java
index 5e6852b8..93d8832b 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/ActivateTownhallRoom.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/ActivateTownhallRoom.java
@@ -17,20 +17,20 @@ import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
-public class ActivateTownhallRoom extends RestCall {
+public class ActivateTownhallRoom extends RestCall {
public ActivateTownhallRoom() {
super(new TypeKey<>(
CallType.UNDEFINED,
EntityType.EXAM_PROCTOR_DATA,
- new TypeReference() {
+ new TypeReference() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetProctorRoomConnectionData.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetProctorRoomConnectionData.java
index 960767b3..932d0f7b 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetProctorRoomConnectionData.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/GetProctorRoomConnectionData.java
@@ -17,20 +17,20 @@ import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
-public class GetProctorRoomConnectionData extends RestCall {
+public class GetProctorRoomConnectionData extends RestCall {
public GetProctorRoomConnectionData() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.EXAM_PROCTOR_DATA,
- new TypeReference() {
+ new TypeReference() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/SendJoinRemoteProctoringRoom.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/SendJoinRemoteProctoringRoom.java
index 5e8fe9fc..50622ed6 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/SendJoinRemoteProctoringRoom.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/api/session/SendJoinRemoteProctoringRoom.java
@@ -17,20 +17,20 @@ import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
-public class SendJoinRemoteProctoringRoom extends RestCall {
+public class SendJoinRemoteProctoringRoom extends RestCall {
public SendJoinRemoteProctoringRoom() {
super(new TypeKey<>(
CallType.UNDEFINED,
EntityType.EXAM_PROCTOR_DATA,
- new TypeReference() {
+ new TypeReference() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java
index 0e449d2f..de5eb00b 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/ProctoringGUIService.java
@@ -27,7 +27,7 @@ import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.Domain;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ActivateTownhallRoom;
@@ -82,7 +82,7 @@ public class ProctoringGUIService {
public static void setCurrentProctoringWindowData(
final String examId,
- final SEBProctoringConnectionData data) {
+ final SEBProctoringConnection data) {
RWT.getUISession().getHttpSession().setAttribute(
SESSION_ATTR_PROCTORING_DATA,
@@ -103,7 +103,7 @@ public class ProctoringGUIService {
.orElseGet(() -> null);
}
- public Result registerNewSingleProcotringRoom(
+ public Result registerNewSingleProcotringRoom(
final String examId,
final String roomName,
final String subject,
@@ -111,8 +111,8 @@ public class ProctoringGUIService {
return this.restService.getBuilder(SendJoinRemoteProctoringRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
- .withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, roomName)
- .withFormParam(SEBProctoringConnectionData.ATTR_SUBJECT, subject)
+ .withFormParam(SEBProctoringConnection.ATTR_ROOM_NAME, roomName)
+ .withFormParam(SEBProctoringConnection.ATTR_SUBJECT, subject)
.withFormParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
.call()
.map(connection -> {
@@ -122,13 +122,13 @@ public class ProctoringGUIService {
});
}
- public Result registerTownhallRoom(
+ public Result registerTownhallRoom(
final String examId,
final String subject) {
return this.restService.getBuilder(ActivateTownhallRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
- .withFormParam(SEBProctoringConnectionData.ATTR_SUBJECT, subject)
+ .withFormParam(SEBProctoringConnection.ATTR_SUBJECT, subject)
.call()
.map(connection -> {
this.rooms.put(
@@ -139,7 +139,7 @@ public class ProctoringGUIService {
});
}
- public Result registerNewProcotringRoom(
+ public Result registerNewProcotringRoom(
final String examId,
final String roomName,
final String subject,
@@ -147,8 +147,8 @@ public class ProctoringGUIService {
return this.restService.getBuilder(SendJoinRemoteProctoringRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
- .withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, roomName)
- .withFormParam(SEBProctoringConnectionData.ATTR_SUBJECT, subject)
+ .withFormParam(SEBProctoringConnection.ATTR_ROOM_NAME, roomName)
+ .withFormParam(SEBProctoringConnection.ATTR_SUBJECT, subject)
.withFormParam(
API.EXAM_API_SEB_CONNECTION_TOKEN,
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR_CHAR))
@@ -172,7 +172,7 @@ public class ProctoringGUIService {
}
this.restService.getBuilder(SendJoinRemoteProctoringRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
- .withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, room)
+ .withFormParam(SEBProctoringConnection.ATTR_ROOM_NAME, room)
.withFormParam(
API.EXAM_API_SEB_CONNECTION_TOKEN,
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR_CHAR))
@@ -288,11 +288,11 @@ public class ProctoringGUIService {
public static class ProctoringWindowData {
public final String examId;
- public final SEBProctoringConnectionData connectionData;
+ public final SEBProctoringConnection connectionData;
protected ProctoringWindowData(
final String examId,
- final SEBProctoringConnectionData connectionData) {
+ final SEBProctoringConnection connectionData) {
this.examId = examId;
this.connectionData = connectionData;
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java
index 206c9a78..ccc8a806 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxLmsAPITemplate.java
@@ -28,6 +28,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
+import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
@@ -65,7 +66,11 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
@Override
public Result> getQuizzes(final FilterMap filterMap) {
- return this.openEdxCourseAccess.getQuizzes(filterMap);
+ return this.openEdxCourseAccess
+ .getQuizzes(filterMap)
+ .map(quizzes -> quizzes.stream()
+ .filter(LmsAPIService.quizFilterPredicate(filterMap))
+ .collect(Collectors.toList()));
}
@Override
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java
index ef71a33d..8cd903bc 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleLmsAPITemplate.java
@@ -28,6 +28,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
+import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException;
@@ -67,7 +68,11 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
@Override
public Result> getQuizzes(final FilterMap filterMap) {
- return this.moodleCourseAccess.getQuizzes(filterMap);
+ return this.moodleCourseAccess
+ .getQuizzes(filterMap)
+ .map(quizzes -> quizzes.stream()
+ .filter(LmsAPIService.quizFilterPredicate(filterMap))
+ .collect(Collectors.toList()));
}
@Override
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java
index aa9fcb60..965bc52a 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java
@@ -10,46 +10,51 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
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.SEBProctoringConnectionData;
+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;
public interface ExamProctoringService {
+ /** Get the proctoring server type of the specific implementation
+ *
+ * @return the proctoring service type of the specific implementation */
ProctoringServerType getType();
+ /** Use this to test the proctoring service settings against the remote proctoring server.
+ *
+ * @param examProctoring the settings to test
+ * @return Result refer to true if the settings are correct and the proctoring server can be accessed. */
Result testExamProctoring(final ProctoringSettings examProctoring);
- Result createProctorPublicRoomConnection(
+ /** Used to get the proctor's room connection data.
+ *
+ * @param proctoringSettings the proctoring settings
+ * @param roomName the name of the room
+ * @param subject name of the room
+ * @return SEBProctoringConnectionData that contains all connection data */
+ Result createProctorPublicRoomConnection(
final ProctoringSettings proctoringSettings,
final String roomName,
final String subject);
- Result getClientExamCollectingRoomConnectionData(
- final ProctoringSettings proctoringSettings,
- final String connectionToken);
-
- Result getClientExamCollectingRoomConnectionData(
+ Result getClientExamCollectingRoomConnection(
final ProctoringSettings proctoringSettings,
final ClientConnection connection);
- Result getClientExamCollectingRoomConnectionData(
+ Result getClientExamCollectingRoomConnection(
final ProctoringSettings proctoringSettings,
final String connectionToken,
final String roomName,
final String subject);
- Result getClientRoomConnectionData(
- final ProctoringSettings proctoringSettings,
- final String connectionToken);
-
- Result getClientRoomConnectionData(
+ Result getClientRoomConnection(
final ProctoringSettings examProctoring,
final String connectionToken,
final String roomName,
final String subject);
- Result createProctoringConnectionData(
+ Result createProctoringConnection(
final ProctoringServerType proctoringServerType,
final String connectionToken,
final String url,
@@ -59,7 +64,8 @@ public interface ExamProctoringService {
final String clientKey,
final String roomName,
final String subject,
- final Long expTime);
+ final Long expTime,
+ final boolean moderator);
Result createClientAccessToken(
final ProctoringSettings proctoringSettings,
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java
index e58eac80..63b21282 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java
@@ -25,7 +25,7 @@ import ch.ethz.seb.sebserver.gbl.Constants;
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.ProctoringServerType;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+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.profile.WebServiceProfile;
@@ -46,7 +46,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
private static final String JITSI_ACCESS_TOKEN_PAYLOAD =
- "{\"context\":{\"user\":{\"name\":\"%s\"}},\"iss\":\"%s\",\"aud\":\"%s\",\"sub\":\"%s\",\"room\":\"%s\"%s}";
+ "{\"context\":{\"user\":{\"name\":\"%s\"}},\"iss\":\"%s\",\"aud\":\"%s\",\"sub\":\"%s\",\"room\":\"%s\"%s%s}";
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
private final AuthorizationService authorizationService;
@@ -77,13 +77,13 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
}
@Override
- public Result createProctorPublicRoomConnection(
+ public Result createProctorPublicRoomConnection(
final ProctoringSettings proctoringSettings,
final String roomName,
final String subject) {
return Result.tryCatch(() -> {
- return createProctoringConnectionData(
+ return createProctoringConnection(
proctoringSettings.serverType,
null,
proctoringSettings.serverURL,
@@ -93,25 +93,14 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-server",
roomName,
subject,
- forExam(proctoringSettings))
+ forExam(proctoringSettings),
+ true)
.getOrThrow();
});
}
@Override
- public Result getClientExamCollectingRoomConnectionData(
- final ProctoringSettings proctoringSettings,
- final String connectionToken) {
-
- return this.examSessionService
- .getConnectionData(connectionToken)
- .flatMap(connection -> getClientExamCollectingRoomConnectionData(
- proctoringSettings,
- connection.clientConnection));
- }
-
- @Override
- public Result getClientExamCollectingRoomConnectionData(
+ public Result getClientExamCollectingRoomConnection(
final ProctoringSettings proctoringSettings,
final ClientConnection connection) {
@@ -121,7 +110,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
.getRoomName(connection.getRemoteProctoringRoomId())
.getOrThrow();
- return createProctoringConnectionData(
+ return createProctoringConnection(
proctoringSettings.serverType,
null,
proctoringSettings.serverURL,
@@ -131,13 +120,14 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client",
roomName,
connection.userSessionId,
- forExam(proctoringSettings))
+ forExam(proctoringSettings),
+ false)
.getOrThrow();
});
}
@Override
- public Result getClientExamCollectingRoomConnectionData(
+ public Result getClientExamCollectingRoomConnection(
final ProctoringSettings proctoringSettings,
final String connectionToken,
final String roomName,
@@ -148,7 +138,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
.getConnectionData(connectionToken)
.getOrThrow();
- return createProctoringConnectionData(
+ return createProctoringConnection(
proctoringSettings.serverType,
null,
proctoringSettings.serverURL,
@@ -158,35 +148,14 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client",
roomName,
subject,
- forExam(proctoringSettings))
+ forExam(proctoringSettings),
+ false)
.getOrThrow();
});
}
@Override
- public Result getClientRoomConnectionData(
- final ProctoringSettings proctoringSettings,
- final String connectionToken) {
-
- return Result.tryCatch(() -> this.examSessionService
- .getConnectionData(connectionToken)
- .getOrThrow()
-
- ).flatMap(clientConnection -> {
- final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
- final String roomName = urlEncoder.encodeToString(
- Utils.toByteArray(clientConnection.clientConnection.connectionToken));
-
- return getClientRoomConnectionData(
- proctoringSettings,
- connectionToken,
- roomName,
- clientConnection.clientConnection.userSessionId);
- });
- }
-
- @Override
- public Result getClientRoomConnectionData(
+ public Result getClientRoomConnection(
final ProctoringSettings proctoringSettings,
final String connectionToken,
final String roomName,
@@ -199,7 +168,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
.getConnectionData(connectionToken)
.getOrThrow();
- return createProctoringConnectionData(
+ return createProctoringConnection(
proctoringSettings.serverType,
connectionToken,
proctoringSettings.serverURL,
@@ -209,14 +178,15 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client",
roomName,
subject,
- expTime)
+ expTime,
+ false)
.getOrThrow();
});
}
@Override
- public Result createProctoringConnectionData(
+ public Result createProctoringConnection(
final ProctoringServerType proctoringServerType,
final String connectionToken,
final String url,
@@ -226,7 +196,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
final String clientKey,
final String roomName,
final String subject,
- final Long expTime) {
+ final Long expTime,
+ final boolean moderator) {
return Result.tryCatch(() -> {
@@ -242,9 +213,10 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
clientKey,
roomName,
expTime,
- host);
+ host,
+ moderator);
- return new SEBProctoringConnectionData(
+ return new SEBProctoringConnection(
proctoringServerType,
connectionToken,
host,
@@ -279,34 +251,27 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client",
roomName,
forExam(proctoringSettings),
- host);
+ host,
+ false);
});
}
- private String internalCreateAccessToken(
+ protected String internalCreateAccessToken(
final String appKey,
final CharSequence appSecret,
final String clientName,
final String clientKey,
final String roomName,
final Long expTime,
- final String host) throws NoSuchAlgorithmException, InvalidKeyException {
+ final String host,
+ final boolean moderator) throws NoSuchAlgorithmException, InvalidKeyException {
final StringBuilder builder = new StringBuilder();
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
final String jwtHeaderPart = urlEncoder
.encodeToString(JITSI_ACCESS_TOKEN_HEADER.getBytes(StandardCharsets.UTF_8));
- final String jwtPayload = String.format(
- JITSI_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""),
- clientName,
- appKey,
- clientKey,
- host,
- roomName,
- (expTime != null)
- ? String.format(",\"exp\":%s", String.valueOf(expTime))
- : "");
+ final String jwtPayload = createPayload(appKey, clientName, clientKey, roomName, expTime, host, moderator);
final String jwtPayloadPart = urlEncoder
.encodeToString(jwtPayload.getBytes(StandardCharsets.UTF_8));
final String message = jwtHeaderPart + "." + jwtPayloadPart;
@@ -324,6 +289,31 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
return builder.toString();
}
+ protected String createPayload(
+ final String appKey,
+ final String clientName,
+ final String clientKey,
+ final String roomName,
+ final Long expTime,
+ final String host,
+ final boolean moderator) {
+
+ final String jwtPayload = String.format(
+ JITSI_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""),
+ clientName,
+ appKey,
+ clientKey,
+ host,
+ roomName,
+ (moderator)
+ ? ",\"moderator\":true"
+ : ",\"moderator\":false",
+ (expTime != null)
+ ? String.format(",\"exp\":%s", String.valueOf(expTime))
+ : "");
+ return jwtPayload;
+ }
+
private long forExam(final ProctoringSettings examProctoring) {
if (examProctoring.examId == null) {
throw new IllegalStateException("Missing exam identifier from ExamProctoring data");
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamProctoringRoomServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamProctoringRoomServiceImpl.java
index 3ee5c152..3c90f48f 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamProctoringRoomServiceImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamProctoringRoomServiceImpl.java
@@ -20,7 +20,7 @@ import org.springframework.stereotype.Service;
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.SEBProctoringConnectionData;
+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.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
@@ -196,9 +196,9 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getExamProctoring(examId)
.getOrThrow();
- final SEBProctoringConnectionData proctoringData =
+ final SEBProctoringConnection proctoringData =
this.examAdminService.getExamProctoringService(proctoringSettings.serverType)
- .flatMap(s -> s.getClientExamCollectingRoomConnectionData(
+ .flatMap(s -> s.getClientExamCollectingRoomConnection(
proctoringSettings,
connectionToken,
roomName,
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java
index 7464140d..47caaa92 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamProctoringController.java
@@ -9,6 +9,8 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.Arrays;
+import java.util.Base64;
+import java.util.Base64.Encoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -31,13 +33,14 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+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.ClientInstruction;
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.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.UserService;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
@@ -107,15 +110,16 @@ public class ExamProctoringController {
path = API.MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
+
produces = MediaType.APPLICATION_JSON_VALUE)
- public SEBProctoringConnectionData getProctorRoomData(
+ public SEBProctoringConnection getProctorRoomData(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
- @RequestParam(name = SEBProctoringConnectionData.ATTR_ROOM_NAME, required = true) final String roomName,
- @RequestParam(name = SEBProctoringConnectionData.ATTR_SUBJECT, required = false) final String subject) {
+ @RequestParam(name = SEBProctoringConnection.ATTR_ROOM_NAME, required = true) final String roomName,
+ @RequestParam(name = SEBProctoringConnection.ATTR_SUBJECT, required = false) final String subject) {
checkAccess(institutionId, examId);
@@ -217,27 +221,12 @@ public class ExamProctoringController {
checkAccess(institutionId, examId);
- final ProctoringSettings settings = this.examSessionService
+ final ProctoringSettings proctoringSettings = this.examSessionService
.getRunningExam(examId)
.flatMap(this.examAdminService::getExamProctoring)
.getOrThrow();
- final ExamProctoringService examProctoringService = this.examAdminService
- .getExamProctoringService(settings.serverType)
- .getOrThrow();
-
- Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
- .stream()
- .forEach(connectionToken -> {
- examProctoringService
- .getClientExamCollectingRoomConnectionData(
- settings,
- connectionToken)
- .flatMap(data -> this.sendJoinInstruction(examId, connectionToken, data))
- .onError(error -> log.error("Failed to send rejoin for: {} cause: {}",
- connectionToken,
- error.getMessage()));
- });
+ sendJoinInstructions(connectionTokens, proctoringSettings);
}
@RequestMapping(
@@ -245,17 +234,17 @@ public class ExamProctoringController {
+ API.EXAM_PROCTORING_JOIN_ROOM_PATH_SEGMENT,
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
- public SEBProctoringConnectionData sendJoinProctoringRoomToClients(
+ public SEBProctoringConnection sendJoinProctoringRoomToClients(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
@RequestParam(
- name = SEBProctoringConnectionData.ATTR_ROOM_NAME,
+ name = SEBProctoringConnection.ATTR_ROOM_NAME,
required = true) final String roomName,
@RequestParam(
- name = SEBProctoringConnectionData.ATTR_SUBJECT,
+ name = SEBProctoringConnection.ATTR_SUBJECT,
required = false) final String subject,
@RequestParam(
name = API.EXAM_API_SEB_CONNECTION_TOKEN,
@@ -273,39 +262,30 @@ public class ExamProctoringController {
.getOrThrow();
if (StringUtils.isNotBlank(connectionTokens)) {
- final boolean single = connectionTokens.contains(Constants.LIST_SEPARATOR);
- (single
- ? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
- : Arrays.asList(connectionTokens))
- .stream()
- .forEach(connectionToken -> {
- final SEBProctoringConnectionData data = (single)
- ? examProctoringService
- .getClientRoomConnectionData(settings, connectionToken)
- .onError(error -> log.error(
- "Failed to get client room connection data for {} cause: {}",
- connectionToken,
- error.getMessage()))
- .get()
- : examProctoringService
- .getClientRoomConnectionData(
- settings,
- connectionToken,
- roomName,
- (StringUtils.isNotBlank(subject)) ? subject : roomName)
- .onError(error -> log.error(
- "Failed to get client room connection data for {} cause: {}",
- connectionToken,
- error.getMessage()))
- .get();
- if (data != null) {
- sendJoinInstruction(examId, connectionToken, data)
- .onError(error -> log.error(
- "Failed to send proctoring leave instruction to client: {} cause: {}",
- connectionToken,
- error.getMessage()));
- }
- });
+
+ 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(
@@ -315,6 +295,16 @@ public class ExamProctoringController {
.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(
path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_PROCTORING_TOWNHALL_ROOM_DATA,
@@ -338,14 +328,14 @@ public class ExamProctoringController {
+ API.EXAM_PROCTORING_ACTIVATE_TOWNHALL_ROOM,
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
- public SEBProctoringConnectionData activateTownhall(
+ public SEBProctoringConnection activateTownhall(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
@RequestParam(
- name = SEBProctoringConnectionData.ATTR_SUBJECT,
+ name = SEBProctoringConnection.ATTR_SUBJECT,
required = false) final String subject) {
checkAccess(institutionId, examId);
@@ -371,8 +361,8 @@ public class ExamProctoringController {
.getOrThrow()
.stream()
.forEach(cc -> {
- final SEBProctoringConnectionData data = examProctoringService
- .getClientRoomConnectionData(
+ final SEBProctoringConnection data = examProctoringService
+ .getClientRoomConnection(
settings,
cc.clientConnection.connectionToken,
townhallRoom.name,
@@ -431,7 +421,7 @@ public class ExamProctoringController {
.stream()
.forEach(cc -> {
examProctoringService
- .getClientExamCollectingRoomConnectionData(
+ .getClientExamCollectingRoomConnection(
settings,
cc.clientConnection)
.flatMap(data -> this.sendJoinInstruction(
@@ -455,64 +445,16 @@ public class ExamProctoringController {
return;
}
- if (StringUtils.isNotBlank(connectionTokens)) {
- // we have defined connection tokens to send instructions to
+ final boolean definedClients = StringUtils.isNotBlank(connectionTokens);
+ final boolean inTownhall = this.examProcotringRoomService.getTownhallRoomData(examId).hasValue();
+ final boolean roomSpecified = StringUtils.isNotBlank(roomName);
- final boolean single = connectionTokens.contains(Constants.LIST_SEPARATOR);
- (single
- ? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
- : Arrays.asList(connectionTokens))
- .stream()
- .forEach(connectionToken -> {
- this.sebInstructionService.registerInstruction(
- examId,
- InstructionType.SEB_RECONFIGURE_SETTINGS,
- attributes,
- connectionToken,
- true)
- .onError(error -> log.error(
- "Failed to register reconfiguring instruction for connection: {}",
- connectionToken,
- error));
-
- });
- } else if (this.examProcotringRoomService.getTownhallRoomData(examId).hasValue()) {
- // we are in the town hall so all active connections are involved
-
- this.examSessionService.getAllActiveConnectionData(examId)
- .getOrThrow()
- .stream()
- .forEach(connection -> {
- this.sebInstructionService.registerInstruction(
- examId,
- InstructionType.SEB_RECONFIGURE_SETTINGS,
- attributes,
- connection.clientConnection.connectionToken,
- true)
- .onError(error -> log.error(
- "Failed to register reconfiguring instruction for connection: {}",
- connection.clientConnection.connectionToken,
- error));
- });
- } else if (StringUtils.isNotBlank(roomName)) {
- // we have a room name so all connection of this room are involved
-
- this.examProcotringRoomService.getRoomConnections(examId, roomName)
- .getOrThrow()
- .stream()
- .filter(ExamSessionService.ACTIVE_CONNECTION_FILTER)
- .forEach(connection -> {
- this.sebInstructionService.registerInstruction(
- examId,
- InstructionType.SEB_RECONFIGURE_SETTINGS,
- attributes,
- connection.connectionToken,
- true)
- .onError(error -> log.error(
- "Failed to register reconfiguring instruction for connection: {}",
- connection.connectionToken,
- error));
- });
+ if (definedClients) {
+ sendBroadcastInstructionsToClients(examId, connectionTokens, attributes);
+ } else if (inTownhall) {
+ sendBroadcastInstructionToClientsInExam(examId, attributes);
+ } else if (roomSpecified) {
+ sendBroadcastInstructionToClientsInRoom(examId, roomName, attributes);
} else {
throw new RuntimeException("API attribute validation error: missing "
+ Domain.REMOTE_PROCTORING_ROOM.ATTR_ID + " and/or" +
@@ -520,12 +462,111 @@ public class ExamProctoringController {
}
}
+ private void sendBroadcastInstructionsToClients(final Long examId, final String connectionTokens,
+ final Map attributes) {
+ final boolean single = connectionTokens.contains(Constants.LIST_SEPARATOR);
+ (single
+ ? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
+ : Arrays.asList(connectionTokens))
+ .stream()
+ .forEach(connectionToken -> {
+ this.sebInstructionService.registerInstruction(
+ examId,
+ InstructionType.SEB_RECONFIGURE_SETTINGS,
+ attributes,
+ connectionToken,
+ true)
+ .onError(error -> log.error(
+ "Failed to register reconfiguring instruction for connection: {}",
+ connectionToken,
+ error));
+
+ });
+ }
+
+ private void sendBroadcastInstructionToClientsInExam(final Long examId, final Map attributes) {
+ this.examSessionService
+ .getAllActiveConnectionData(examId)
+ .getOrThrow()
+ .stream()
+ .forEach(connection -> {
+ this.sebInstructionService.registerInstruction(
+ examId,
+ InstructionType.SEB_RECONFIGURE_SETTINGS,
+ attributes,
+ connection.clientConnection.connectionToken,
+ true)
+ .onError(error -> log.error(
+ "Failed to register reconfiguring instruction for connection: {}",
+ connection.clientConnection.connectionToken,
+ error));
+ });
+ }
+
+ private void sendBroadcastInstructionToClientsInRoom(
+ final Long examId,
+ final String roomName,
+ final Map attributes) {
+
+ this.examProcotringRoomService
+ .getRoomConnections(examId, roomName)
+ .getOrThrow()
+ .stream()
+ .filter(ExamSessionService.ACTIVE_CONNECTION_FILTER)
+ .forEach(connection -> {
+ this.sebInstructionService.registerInstruction(
+ examId,
+ InstructionType.SEB_RECONFIGURE_SETTINGS,
+ attributes,
+ connection.connectionToken,
+ true)
+ .onError(error -> log.error(
+ "Failed to register reconfiguring instruction for connection: {}",
+ connection.connectionToken,
+ error));
+ });
+ }
+
+ 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 sendJoinInstruction(
final Long examId,
final String connectionToken,
- final SEBProctoringConnectionData data) {
+ final SEBProctoringConnection data) {
final Map attributes = new HashMap<>();
+
attributes.put(
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.SERVICE_TYPE,
ProctoringSettings.ProctoringServerType.JITSI_MEET.name());
@@ -541,6 +582,7 @@ public class ExamProctoringController {
attributes.put(
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.JITSI_TOKEN,
data.accessToken);
+
return this.sebInstructionService.registerInstruction(
examId,
InstructionType.SEB_PROCTORING,
diff --git a/src/main/resources/config/application-gui.properties b/src/main/resources/config/application-gui.properties
index 8e729d65..d1e795d6 100644
--- a/src/main/resources/config/application-gui.properties
+++ b/src/main/resources/config/application-gui.properties
@@ -31,6 +31,8 @@ sebserver.gui.webservice.moodle-lms-enabled=true
sebserver.gui.seb.client.config.download.filename=SEBServerSettings.seb
sebserver.gui.seb.exam.config.download.filename=SEBExamSettings.seb
+sebserver.gui.filter.date.from.years=2
+
# remote proctoring
sebserver.gui.remote.proctoring.entrypoint=/remote-proctoring
sebserver.gui.remote.proctoring.api-servler.endpoint=/remote-view-servlet
diff --git a/src/main/resources/config/sql/base/V5_2__insert_new_security_settings_v.1.1.sql b/src/main/resources/config/sql/base/V5_2__insert_new_security_settings_v.1.1.sql
new file mode 100644
index 00000000..18207a50
--- /dev/null
+++ b/src/main/resources/config/sql/base/V5_2__insert_new_security_settings_v.1.1.sql
@@ -0,0 +1,36 @@
+INSERT IGNORE INTO configuration_attribute VALUES
+ (318, 'sebServiceIgnore', 'CHECKBOX', null, null, null, null, 'true'),
+ (319, 'allowApplicationLog', 'CHECKBOX', null, null, null, null, 'false'),
+ (320, 'showApplicationLogButton', 'CHECKBOX', null, null, null, null, 'false'),
+ (321, 'enableWindowsUpdate', 'CHECKBOX', null, null, null, null, 'false'),
+ (322, 'enableChromeNotifications', 'CHECKBOX', null, null, null, null, 'false')
+ ;
+
+
+UPDATE orientation SET y_position='13', width='12' WHERE id='305';
+UPDATE orientation SET y_position='16', width='10' WHERE id='306';
+UPDATE orientation SET y_position='17', width='10' WHERE id='307';
+UPDATE orientation SET y_position='18' WHERE id='317';
+UPDATE orientation SET x_position='0', y_position='9' WHERE id='301';
+UPDATE orientation SET x_position='3', y_position='10', width='4' WHERE id='501';
+UPDATE orientation SET x_position='3', y_position='11', width='4' WHERE id='304';
+UPDATE orientation SET x_position='3', y_position='12', width='4' WHERE id='302';
+UPDATE orientation SET group_id='sebService', x_position='4', y_position='6', width='3' WHERE id='303';
+UPDATE orientation SET group_id='sebService', y_position='2', width='7', title='TOP' WHERE id='300';
+UPDATE orientation SET y_position='3' WHERE id='309';
+UPDATE orientation SET y_position='4' WHERE id='310';
+UPDATE orientation SET y_position='5' WHERE id='311';
+UPDATE orientation SET y_position='6' WHERE id='312';
+UPDATE orientation SET y_position='7' WHERE id='313';
+UPDATE orientation SET y_position='8' WHERE id='314';
+UPDATE orientation SET y_position='11' WHERE id='315';
+UPDATE orientation SET y_position='12' WHERE id='316';
+
+
+INSERT IGNORE INTO orientation VALUES
+ (318, 318, 0, 9, 'sebService', 0, 0, 7, 1, 'NONE'),
+ (319, 319, 0, 9, 'logging', 0, 14, 5, 1, 'NONE'),
+ (320, 320, 0, 9, 'logging', 0, 15, 5, 1, 'NONE'),
+ (321, 321, 0, 9, 'sebService', 0, 6, 4, 1, 'NONE'),
+ (322, 322, 0, 9, 'sebService', 0, 7, 4, 1, 'NONE')
+ ;
\ No newline at end of file
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index bada0fa6..862ff8e0 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -1166,11 +1166,15 @@ sebserver.examconfig.props.label.RTSPUsername=Username
sebserver.examconfig.props.label.RTSPPassword=Password
-sebserver.examconfig.props.group.servicePolicy=SEB Service policy
+sebserver.examconfig.props.group.sebService=SEB Service (Win)
+sebserver.examconfig.props.label.sebServicePolicy=IMPORTANT: The SEB Service changes aspects of the system configuration.
Some anti-virus software providers might falsely identify its operation as malicious,
thus it is not recommended to use the SEB Service in uncontrolled environments (e.g. BYOD).
SEB Service policy
sebserver.examconfig.props.label.sebServicePolicy.0=allow to run SEB without service
sebserver.examconfig.props.label.sebServicePolicy.1=display warning when service is not running
sebserver.examconfig.props.label.sebServicePolicy.2=allow to use SEB only with service
sebserver.examconfig.props.label.sebServicePolicy.tooltip=Policy that applies when an exam client doesn't have the SEB client running
+sebserver.examconfig.props.label.sebServiceIgnore=Ignore SEB Service
+sebserver.examconfig.props.label.enableWindowsUpdate=Allow Windows Update to run while SEB is running
+sebserver.examconfig.props.label.enableChromeNotifications=Allow notifications from Chrome browsers
sebserver.examconfig.props.group.kioskMode=Kiosk Mode
sebserver.examconfig.props.label.kioskMode.tooltip=The kiosk mode setting reflects how the computer is locked down in SEB
@@ -1232,6 +1236,8 @@ sebserver.examconfig.props.label.logLevel.3=Debug
sebserver.examconfig.props.label.logLevel.3.tooltip=Debug is reserved for information which is only necessary for in-deep program code debugging
sebserver.examconfig.props.label.logLevel.4=Verbose
sebserver.examconfig.props.label.logLevel.4.tooltip=Verbose level contains events of all levels
+sebserver.examconfig.props.label.allowApplicationLog=Allow access to application log (Win)
+sebserver.examconfig.props.label.showApplicationLogButton=Show log button on taskbar (Win)
sebserver.examconfig.props.group.registry=While running SEB
sebserver.examconfig.props.group.registry.tooltip=Options in the Windows Security Screen invoked by Ctrl-Alt-Del
diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java
index 311d8b0d..4b138e37 100644
--- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java
+++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java
@@ -11,22 +11,59 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
import org.junit.Test;
import org.mockito.Mockito;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ProctoringServerType;
-import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
+import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnection;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
public class ExamJITSIProctoringServiceTest {
+ @Test
+ public void testTokenPayload() throws InvalidKeyException, NoSuchAlgorithmException {
+ final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
+ Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
+ final ExamJITSIProctoringService examJITSIProctoringService =
+ new ExamJITSIProctoringService(null, null, null, cryptorMock);
+
+ String accessToken = examJITSIProctoringService.createPayload(
+ "test-app",
+ "Test Name",
+ "test-client",
+ "SomeRoom",
+ 1609459200L,
+ "https://test.ch",
+ false);
+
+ assertEquals(
+ "{\"context\":{\"user\":{\"name\":\"Test Name\"}},\"iss\":\"test-app\",\"aud\":\"test-client\",\"sub\":\"https://test.ch\",\"room\":\"SomeRoom\",\"moderator\":false,\"exp\":1609459200}",
+ accessToken);
+
+ accessToken = examJITSIProctoringService.createPayload(
+ "test-app",
+ "Test Name",
+ "test-client",
+ "SomeRoom",
+ 1609459200L,
+ "https://test.ch",
+ true);
+
+ assertEquals(
+ "{\"context\":{\"user\":{\"name\":\"Test Name\"}},\"iss\":\"test-app\",\"aud\":\"test-client\",\"sub\":\"https://test.ch\",\"room\":\"SomeRoom\",\"moderator\":true,\"exp\":1609459200}",
+ accessToken);
+ }
+
@Test
public void testCreateProctoringURL() {
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
final ExamJITSIProctoringService examJITSIProctoringService =
new ExamJITSIProctoringService(null, null, null, cryptorMock);
- final SEBProctoringConnectionData data = examJITSIProctoringService.createProctoringConnectionData(
+ final SEBProctoringConnection data = examJITSIProctoringService.createProctoringConnection(
ProctoringServerType.JITSI_MEET,
"connectionToken",
"https://seb-jitsi.example.ch",
@@ -36,15 +73,17 @@ public class ExamJITSIProctoringServiceTest {
"test-client",
"SomeRoom",
"Subject",
- 1609459200L)
+ 1609459200L,
+ true)
.getOrThrow();
assertNotNull(data);
assertEquals(
"https://seb-jitsi.example.ch",
data.serverURL);
+
assertEquals(
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4",
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwibW9kZXJhdG9yIjp0cnVlLCJleHAiOjE2MDk0NTkyMDB9.poOwfCsRjNqCizEQM3qFFWbjuX0bZLer3cqlbaPK9wc",
data.accessToken);
}