SEBSERV-139 implementing join-in room in overall monitoring view

This commit is contained in:
anhefti 2020-08-27 10:37:56 +02:00
parent a91192ecbb
commit 3c0c8a4c41
22 changed files with 740 additions and 187 deletions

View file

@ -13,8 +13,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SEBClientProctoringConnectionData { public class SEBProctoringConnectionData {
public static final String ATTR_CONNECTION_TOKEN = "connectionToken";
public static final String ATTR_SERVER_HOST = "serverHost"; public static final String ATTR_SERVER_HOST = "serverHost";
public static final String ATTR_SERVER_URL = "serverURL"; public static final String ATTR_SERVER_URL = "serverURL";
public static final String ATTR_ROOM_NAME = "roomName"; public static final String ATTR_ROOM_NAME = "roomName";
@ -22,6 +23,9 @@ public class SEBClientProctoringConnectionData {
public static final String ATTR_ACCESS_TOKEN = "accessToken"; public static final String ATTR_ACCESS_TOKEN = "accessToken";
public static final String ATTR_CONNECTION_URL = "connectionURL"; public static final String ATTR_CONNECTION_URL = "connectionURL";
@JsonProperty(ATTR_CONNECTION_TOKEN)
public final String connectionToken;
@JsonProperty(ATTR_SERVER_HOST) @JsonProperty(ATTR_SERVER_HOST)
public final String serverHost; public final String serverHost;
@ -37,24 +41,25 @@ public class SEBClientProctoringConnectionData {
@JsonProperty(ATTR_ACCESS_TOKEN) @JsonProperty(ATTR_ACCESS_TOKEN)
public final String accessToken; public final String accessToken;
@JsonProperty(ATTR_CONNECTION_URL)
public final String connectionURL;
@JsonCreator @JsonCreator
public SEBClientProctoringConnectionData( public SEBProctoringConnectionData(
@JsonProperty(ATTR_CONNECTION_TOKEN) final String connectionToken,
@JsonProperty(ATTR_SERVER_HOST) final String serverHost, @JsonProperty(ATTR_SERVER_HOST) final String serverHost,
@JsonProperty(ATTR_SERVER_URL) final String serverURL, @JsonProperty(ATTR_SERVER_URL) final String serverURL,
@JsonProperty(ATTR_ROOM_NAME) final String roomName, @JsonProperty(ATTR_ROOM_NAME) final String roomName,
@JsonProperty(ATTR_SUBJECT) final String subject, @JsonProperty(ATTR_SUBJECT) final String subject,
@JsonProperty(ATTR_ACCESS_TOKEN) final String accessToken, @JsonProperty(ATTR_ACCESS_TOKEN) final String accessToken) {
@JsonProperty(ATTR_CONNECTION_URL) final String connectionURL) {
this.connectionToken = connectionToken;
this.serverHost = serverHost; this.serverHost = serverHost;
this.serverURL = serverURL; this.serverURL = serverURL;
this.roomName = roomName; this.roomName = roomName;
this.subject = subject; this.subject = subject;
this.accessToken = accessToken; this.accessToken = accessToken;
this.connectionURL = connectionURL; }
public String getConnectionToken() {
return this.connectionToken;
} }
public String getServerHost() { public String getServerHost() {
@ -69,10 +74,6 @@ public class SEBClientProctoringConnectionData {
return this.serverURL; return this.serverURL;
} }
public String getConnectionURL() {
return this.connectionURL;
}
public String getRoomName() { public String getRoomName() {
return this.roomName; return this.roomName;
} }
@ -94,8 +95,6 @@ public class SEBClientProctoringConnectionData {
builder.append(this.subject); builder.append(this.subject);
builder.append(", accessToken="); builder.append(", accessToken=");
builder.append(this.accessToken); builder.append(this.accessToken);
builder.append(", connectionURL=");
builder.append(this.connectionURL);
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();
} }

View file

@ -22,7 +22,7 @@ import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.WebApplicationContextUtils;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
@ -51,8 +51,8 @@ public class ProctoringServlet extends HttpServlet {
" const options = {\n" + " const options = {\n" +
" parentNode: document.querySelector('#proctoring'),\n" + " parentNode: document.querySelector('#proctoring'),\n" +
" roomName: '%s',\n" + " roomName: '%s',\n" +
// " width: 600,\n" + " width: window.innerWidth,\n" +
" height: 400,\n" + " height: window.innerHeight,\n" +
" jwt: '%s',\n" + " jwt: '%s',\n" +
" configOverwrite: { startAudioOnly: false, startWithAudioMuted: true, startWithVideoMuted: true, disable1On1Mode: true },\n" + " configOverwrite: { startAudioOnly: false, startWithAudioMuted: true, startWithVideoMuted: true, disable1On1Mode: true },\n" +
" interfaceConfigOverwrite: { " + " interfaceConfigOverwrite: { " +
@ -100,8 +100,8 @@ public class ProctoringServlet extends HttpServlet {
return; return;
} }
final SEBClientProctoringConnectionData proctoringConnectionData = final SEBProctoringConnectionData proctoringConnectionData =
(SEBClientProctoringConnectionData) httpSession.getAttribute(SESSION_ATTR_PROCTORING_DATA); (SEBProctoringConnectionData) httpSession.getAttribute(SESSION_ATTR_PROCTORING_DATA);
final String script = String.format( final String script = String.format(
HTML, HTML,

View file

@ -27,7 +27,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.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; 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.ProctoringSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; 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.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
@ -54,7 +54,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorDataForSEBClient; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnectionData;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionDetails; import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionDetails;
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor; import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
@ -277,10 +277,7 @@ public class MonitoringClientConnection implements TemplateComposer {
.withEntityKey(parentEntityKey) .withEntityKey(parentEntityKey)
.withExec(action -> this.openProctorScreen(action, connectionToken)) .withExec(action -> this.openProctorScreen(action, connectionToken))
.noEventPropagation() .noEventPropagation()
.publishIf(() -> proctoringEnabled) .publishIf(() -> proctoringEnabled);
;
} }
// @formatter:off // @formatter:off
@ -295,12 +292,12 @@ public class MonitoringClientConnection implements TemplateComposer {
// @formatter:on // @formatter:on
private PageAction openProctorScreen(final PageAction action, final String connectionToken) { private PageAction openProctorScreen(final PageAction action, final String connectionToken) {
final SEBClientProctoringConnectionData proctoringConnectionData = final SEBProctoringConnectionData proctoringConnectionData = this.pageService.getRestService()
this.pageService.getRestService().getBuilder(GetProctorDataForSEBClient.class) .getBuilder(GetProctorRoomConnectionData.class)
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId) .withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
.withURIVariable(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken) .withQueryParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
.call() .call()
.getOrThrow(); .getOrThrow();
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding(); final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
final String roomName = urlEncoder.encodeToString(Utils.toByteArray(connectionToken)); final String roomName = urlEncoder.encodeToString(Utils.toByteArray(connectionToken));
@ -316,6 +313,9 @@ public class MonitoringClientConnection implements TemplateComposer {
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(), this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
roomName); roomName);
javaScriptExecutor.execute(script); javaScriptExecutor.execute(script);
this.pageService.getCurrentUser()
.getProctoringGUIService()
.registerProctoringWindow(roomName);
return action; return action;
} }

View file

@ -15,6 +15,8 @@ import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.GridLayout;
@ -31,6 +33,8 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; 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.session.ClientConnection; 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.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
@ -38,6 +42,8 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Tuple; import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
import ch.ethz.seb.sebserver.gui.ProctoringServlet;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
@ -53,10 +59,13 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionDataList; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionDataList;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnectionData;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable; import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable;
import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor; import ch.ethz.seb.sebserver.gui.service.session.InstructionProcessor;
import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
@Lazy @Lazy
@Component @Component
@ -80,18 +89,21 @@ public class MonitoringRunningExam implements TemplateComposer {
private final PageService pageService; private final PageService pageService;
private final ResourceService resourceService; private final ResourceService resourceService;
private final InstructionProcessor instructionProcessor; private final InstructionProcessor instructionProcessor;
private final GuiServiceInfo guiServiceInfo;
private final long pollInterval; private final long pollInterval;
protected MonitoringRunningExam( protected MonitoringRunningExam(
final ServerPushService serverPushService, final ServerPushService serverPushService,
final PageService pageService, final PageService pageService,
final InstructionProcessor instructionProcessor, final InstructionProcessor instructionProcessor,
final GuiServiceInfo guiServiceInfo,
@Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval) { @Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval) {
this.serverPushService = serverPushService; this.serverPushService = serverPushService;
this.pageService = pageService; this.pageService = pageService;
this.resourceService = pageService.getResourceService(); this.resourceService = pageService.getResourceService();
this.instructionProcessor = instructionProcessor; this.instructionProcessor = instructionProcessor;
this.guiServiceInfo = guiServiceInfo;
this.pollInterval = pollInterval; this.pollInterval = pollInterval;
} }
@ -145,7 +157,8 @@ public class MonitoringRunningExam implements TemplateComposer {
pageContext, pageContext,
ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION, ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION,
ActionDefinition.MONITOR_EXAM_QUIT_SELECTED, ActionDefinition.MONITOR_EXAM_QUIT_SELECTED,
ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION)); ActionDefinition.MONITOR_EXAM_DISABLE_SELECTED_CONNECTION,
ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM));
this.serverPushService.runServerPush( this.serverPushService.runServerPush(
new ServerPushContext(content, Utils.truePredicate()), new ServerPushContext(content, Utils.truePredicate()),
@ -275,6 +288,136 @@ public class MonitoringRunningExam implements TemplateComposer {
} }
} }
final boolean proctoringEnabled = restService
.getBuilder(GetProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.map(ProctoringSettings::getEnableProctoring)
.getOr(false);
if (proctoringEnabled) {
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_NEW_PROCTOR_ROOM)
.withEntityKey(entityKey)
.withSelect(
clientTable::getSelection,
action -> newProctoringRoom(clientTable, action),
EMPTY_SELECTION_TEXT_KEY)
.noEventPropagation()
.publishIf(privilege, false);
final ProctoringGUIService proctoringGUIService = this.pageService
.getCurrentUser()
.getProctoringGUIService();
proctoringGUIService.roomNames().forEach(roomName -> {
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_VIEW_PROCTOR_ROOM)
.withEntityKey(entityKey)
.withExec(a -> showProctoringRoom(roomName, clientTable, a))
.withNameAttributes(roomName)
.noEventPropagation()
.publish();
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_CLOSE_PROCTOR_ROOM)
.withEntityKey(entityKey)
.withExec(a -> closeProctoringRoom(roomName, clientTable, a))
.withNameAttributes(roomName)
.publish();
});
}
}
// @formatter:off
private static final String OPEN_SINGEL_ROOM_SCRIPT =
"var existingWin = window.open('', '%s', 'height=800,width=1200,location=no,scrollbars=yes,status=no,menubar=yes,toolbar=yes,titlebar=yes');\n" +
"if(existingWin.location.href === 'about:blank'){\n" +
" existingWin.location.href = '%s/proctoring/%s';\n" +
" existingWin.focus();\n" +
"} else {\n" +
" existingWin.focus();\n" +
"}";
// @formatter:on
private PageAction closeProctoringRoom(
final String roomName,
final ClientConnectionTable clientTable,
final PageAction action) {
final ProctoringGUIService proctoringGUIService = this.pageService
.getCurrentUser()
.getProctoringGUIService();
proctoringGUIService.closeRoom(roomName);
return action;
}
private PageAction newProctoringRoom(
final ClientConnectionTable clientTable,
final PageAction action) {
final ProctoringGUIService proctoringGUIService = this.pageService
.getCurrentUser()
.getProctoringGUIService();
final String newRoomName = proctoringGUIService.createNewRoomName();
final Set<String> connectionTokens = clientTable.getConnectionTokens(
ClientConnection.getStatusPredicate(ConnectionStatus.ACTIVE),
true);
proctoringGUIService.registerNewProcotringRoom(
action.getEntityKey().modelId,
newRoomName,
connectionTokens);
this.pageService.pageActionBuilder(action.pageContext())
.newAction(ActionDefinition.MONITOR_EXAM_VIEW_PROCTOR_ROOM)
.withEntityKey(action.getEntityKey())
.withExec(a -> showProctoringRoom(newRoomName, clientTable, a))
.withNameAttributes(newRoomName)
.noEventPropagation()
.publish();
this.pageService.pageActionBuilder(action.pageContext())
.newAction(ActionDefinition.MONITOR_EXAM_CLOSE_PROCTOR_ROOM)
.withEntityKey(action.getEntityKey())
.withExec(a -> closeProctoringRoom(newRoomName, clientTable, a))
.withNameAttributes(newRoomName)
.publish();
return showProctoringRoom(newRoomName, clientTable, action);
}
private PageAction showProctoringRoom(
final String roomName,
final ClientConnectionTable clientTable,
final PageAction action) {
final SEBProctoringConnectionData proctoringConnectionData = this.pageService.getRestService()
.getBuilder(GetProctorRoomConnectionData.class)
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
.withQueryParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, roomName)
.call()
.getOrThrow();
RWT.getUISession().getHttpSession().setAttribute(
ProctoringServlet.SESSION_ATTR_PROCTORING_DATA,
proctoringConnectionData);
final String script = String.format(
OPEN_SINGEL_ROOM_SCRIPT,
roomName,
this.guiServiceInfo.getExternalServerURIBuilder().toUriString(),
roomName);
RWT.getClient()
.getService(JavaScriptExecutor.class)
.execute(script);
this.pageService.getCurrentUser()
.getProctoringGUIService()
.registerProctoringWindow(roomName);
return action;
} }
private static Function<PageAction, PageAction> showStateViewAction( private static Function<PageAction, PageAction> showStateViewAction(

View file

@ -29,7 +29,8 @@ public enum ActionCategory {
LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1), LOGS_USER_ACTIVITY_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1), LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0), VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0),
FILTER(new LocTextKey("sebserver.overall.action.category.filter"), 50); FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.filter"), 50),
PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60);
public final LocTextKey title; public final LocTextKey title;
public final int slotPosition; public final int slotPosition;

View file

@ -684,6 +684,22 @@ public enum ActionDefinition {
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM, PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.FILTER), ActionCategory.FILTER),
MONITOR_EXAM_NEW_PROCTOR_ROOM(
new LocTextKey("sebserver.monitoring.exam.action.newroom"),
ImageIcon.VISIBILITY,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.PROCTORING),
MONITOR_EXAM_VIEW_PROCTOR_ROOM(
new LocTextKey("sebserver.monitoring.exam.action.viewroom"),
ImageIcon.SHOW,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.PROCTORING),
MONITOR_EXAM_CLOSE_PROCTOR_ROOM(
new LocTextKey("sebserver.monitoring.exam.action.closeroom"),
ImageIcon.DELETE,
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.PROCTORING),
LOGS_USER_ACTIVITY_LIST( LOGS_USER_ACTIVITY_LIST(
new LocTextKey("sebserver.logs.activity.userlogs"), new LocTextKey("sebserver.logs.activity.userlogs"),
PageStateDefinitionImpl.USER_ACTIVITY_LOGS), PageStateDefinitionImpl.USER_ACTIVITY_LOGS),
@ -745,4 +761,8 @@ public enum ActionDefinition {
this.category = category; this.category = category;
} }
public LocTextKey getTitle(final Object... args) {
return new LocTextKey(this.title.name, args);
}
} }

View file

@ -81,7 +81,7 @@ public class ActionPane implements TemplateComposer {
final Tree treeForGroup = getTreeForGroup(actionTrees, parent, event.action.definition, true); final Tree treeForGroup = getTreeForGroup(actionTrees, parent, event.action.definition, true);
final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized( final TreeItem actionItem = ActionPane.this.widgetFactory.treeItemLocalized(
treeForGroup, treeForGroup,
event.action.definition.title); event.action.getTitle());
final Image image = event.active final Image image = event.active
? event.action.definition.icon.getImage(parent.getDisplay()) ? event.action.definition.icon.getImage(parent.getDisplay())
@ -114,6 +114,7 @@ public class ActionPane implements TemplateComposer {
continue; continue;
} }
final PageAction action = (PageAction) actionItem.getData(ACTION_EVENT_CALL_KEY);
final Image image = event.activation final Image image = event.activation
? ad.icon.getImage(parent.getDisplay()) ? ad.icon.getImage(parent.getDisplay())
: ad.icon.getGreyedImage(parent.getDisplay()); : ad.icon.getGreyedImage(parent.getDisplay());
@ -122,18 +123,21 @@ public class ActionPane implements TemplateComposer {
actionItem.setForeground(null); actionItem.setForeground(null);
} else { } else {
actionItem.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50))); actionItem.setForeground(new Color(parent.getDisplay(), new RGBA(150, 150, 150, 50)));
ActionPane.this.pageService.getPolyglotPageService().injectI18n(actionItem, ad.title); ActionPane.this.pageService.getPolyglotPageService().injectI18n(
actionItem,
(action != null) ? action.getTitle() : ad.title);
} }
} }
if (event.decoration != null) { if (event.decoration != null) {
final TreeItem actionItemToDecorate = findAction(actionTrees, parent, event.decoration._1); final TreeItem actionItemToDecorate = findAction(actionTrees, parent, event.decoration._1);
final PageAction action = (PageAction) actionItemToDecorate.getData(ACTION_EVENT_CALL_KEY);
if (actionItemToDecorate != null && event.decoration._2 != null) { if (actionItemToDecorate != null && event.decoration._2 != null) {
actionItemToDecorate.setImage(0, actionItemToDecorate.setImage(0,
event.decoration._2.icon.getImage(parent.getDisplay())); event.decoration._2.icon.getImage(parent.getDisplay()));
ActionPane.this.pageService.getPolyglotPageService().injectI18n( ActionPane.this.pageService.getPolyglotPageService().injectI18n(
actionItemToDecorate, actionItemToDecorate,
event.decoration._2.title); (action != null) ? action.getTitle() : event.decoration._2.title);
} }
} }
}); });
@ -250,7 +254,7 @@ public class ActionPane implements TemplateComposer {
final PageAction switchAction = action.getSwitchAction(); final PageAction switchAction = action.getSwitchAction();
if (switchAction != null) { if (switchAction != null) {
final PolyglotPageService polyglotPageService = this.pageService.getPolyglotPageService(); final PolyglotPageService polyglotPageService = this.pageService.getPolyglotPageService();
polyglotPageService.injectI18n(treeItem, switchAction.definition.title); polyglotPageService.injectI18n(treeItem, switchAction.getTitle());
treeItem.setImage(switchAction.definition.icon.getImage(treeItem.getDisplay())); treeItem.setImage(switchAction.definition.icon.getImage(treeItem.getDisplay()));
treeItem.setData(ACTION_EVENT_CALL_KEY, switchAction); treeItem.setData(ACTION_EVENT_CALL_KEY, switchAction);
} }

View file

@ -454,6 +454,7 @@ public interface PageService {
private boolean fireActionEvent = true; private boolean fireActionEvent = true;
private boolean ignoreMoveAwayFromEdit = false; private boolean ignoreMoveAwayFromEdit = false;
private PageAction switchAction; private PageAction switchAction;
private Object[] titleArgs;
protected PageActionBuilder(final PageService pageService, final PageContext pageContext) { protected PageActionBuilder(final PageService pageService, final PageContext pageContext) {
this.pageService = pageService; this.pageService = pageService;
@ -486,7 +487,8 @@ public interface PageService {
exec, exec,
fireActionEvent, fireActionEvent,
ignoreMoveAwayFromEdit, ignoreMoveAwayFromEdit,
switchAction); switchAction,
titleArgs);
} }
public PageActionBuilder publish() { public PageActionBuilder publish() {
@ -520,6 +522,11 @@ public interface PageService {
return this; return this;
} }
public PageActionBuilder withNameAttributes(final Object... attributes) {
this.titleArgs = attributes;
return this;
}
public PageActionBuilder withSelectionSupplier(final Supplier<Set<EntityKey>> selectionSupplier) { public PageActionBuilder withSelectionSupplier(final Supplier<Set<EntityKey>> selectionSupplier) {
this.selectionSupplier = selectionSupplier; this.selectionSupplier = selectionSupplier;
return this; return this;

View file

@ -43,6 +43,7 @@ public final class PageAction {
final boolean fireActionEvent; final boolean fireActionEvent;
final boolean ignoreMoveAwayFromEdit; final boolean ignoreMoveAwayFromEdit;
private PageAction switchAction; private PageAction switchAction;
final Object[] titleArgs;
final LocTextKey successMessage; final LocTextKey successMessage;
@ -56,7 +57,8 @@ public final class PageAction {
final Function<PageAction, PageAction> exec, final Function<PageAction, PageAction> exec,
final boolean fireActionEvent, final boolean fireActionEvent,
final boolean ignoreMoveAwayFromEdit, final boolean ignoreMoveAwayFromEdit,
final PageAction switchAction) { final PageAction switchAction,
final Object[] titleArgs) {
this.definition = definition; this.definition = definition;
this.confirm = confirm; this.confirm = confirm;
@ -68,6 +70,7 @@ public final class PageAction {
this.fireActionEvent = fireActionEvent; this.fireActionEvent = fireActionEvent;
this.ignoreMoveAwayFromEdit = ignoreMoveAwayFromEdit; this.ignoreMoveAwayFromEdit = ignoreMoveAwayFromEdit;
this.switchAction = switchAction; this.switchAction = switchAction;
this.titleArgs = titleArgs;
if (this.switchAction != null) { if (this.switchAction != null) {
this.switchAction.switchAction = this; this.switchAction.switchAction = this;
} }
@ -91,6 +94,14 @@ public final class PageAction {
return Constants.EMPTY_NOTE; return Constants.EMPTY_NOTE;
} }
public LocTextKey getTitle() {
if (this.titleArgs != null) {
return this.definition.getTitle(this.titleArgs);
} else {
return this.definition.title;
}
}
public PageAction getSwitchAction() { public PageAction getSwitchAction() {
return this.switchAction; return this.switchAction;
} }
@ -277,7 +288,8 @@ public final class PageAction {
source.exec, source.exec,
source.fireActionEvent, source.fireActionEvent,
source.ignoreMoveAwayFromEdit, source.ignoreMoveAwayFromEdit,
source.switchAction); source.switchAction,
source.titleArgs);
} }
} }

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 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.remote.webservice.api.session;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetProctorRoomConnectionData extends RestCall<SEBProctoringConnectionData> {
public GetProctorRoomConnectionData() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.EXAM_PROCTOR_DATA,
new TypeReference<SEBProctoringConnectionData>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_MONITORING_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.PROCTOR_PATH_SEGMENT);
}
}

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session; package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session;
import java.util.List;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -17,27 +19,27 @@ import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy @Lazy
@Component @Component
@GuiProfile @GuiProfile
public class GetProctorDataForSEBClient extends RestCall<SEBClientProctoringConnectionData> { public class SEBClientsJoinProctorRoom extends RestCall<List<SEBProctoringConnectionData>> {
public GetProctorDataForSEBClient() { public SEBClientsJoinProctorRoom() {
super(new TypeKey<>( super(new TypeKey<>(
CallType.GET_SINGLE, CallType.GET_SINGLE,
EntityType.EXAM_PROCTOR_DATA, EntityType.EXAM_PROCTOR_DATA,
new TypeReference<SEBClientProctoringConnectionData>() { new TypeReference<List<SEBProctoringConnectionData>>() {
}), }),
HttpMethod.GET, HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_MONITORING_ENDPOINT API.EXAM_MONITORING_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT + API.MODEL_ID_VAR_PATH_SEGMENT
+ API.PROCTOR_PATH_SEGMENT + API.PROCTOR_PATH_SEGMENT
+ API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT); + API.PROCTOR_JOIN_ROOM_PATH_SEGMENT);
} }
} }

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 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.remote.webservice.api.session;
import java.util.List;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class SEBClientsLeaveProctorRoom extends RestCall<List<SEBProctoringConnectionData>> {
public SEBClientsLeaveProctorRoom() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.EXAM_PROCTOR_DATA,
new TypeReference<List<SEBProctoringConnectionData>>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_MONITORING_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.PROCTOR_PATH_SEGMENT
+ API.PROCTOR_LEAVE_ROOM_PATH_SEGMENT);
}
}

View file

@ -34,6 +34,8 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile; import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.session.ProctoringGUIService;
@Component @Component
@GuiProfile @GuiProfile
@ -46,10 +48,19 @@ public class CurrentUser {
private SEBServerAuthorizationContext authContext = null; private SEBServerAuthorizationContext authContext = null;
private Map<RoleTypeKey, Privilege> privileges = null; private Map<RoleTypeKey, Privilege> privileges = null;
private final Map<String, String> attributes; private final Map<String, String> attributes;
private final ProctoringGUIService proctoringGUIService;
public CurrentUser(
final AuthorizationContextHolder authorizationContextHolder,
final RestService restService) {
public CurrentUser(final AuthorizationContextHolder authorizationContextHolder) {
this.authorizationContextHolder = authorizationContextHolder; this.authorizationContextHolder = authorizationContextHolder;
this.attributes = new HashMap<>(); this.attributes = new HashMap<>();
this.proctoringGUIService = new ProctoringGUIService(restService);
}
public ProctoringGUIService getProctoringGUIService() {
return this.proctoringGUIService;
} }
public void putAttribute(final String name, final String value) { public void putAttribute(final String name, final String value) {
@ -179,6 +190,7 @@ public class CurrentUser {
this.attributes.clear(); this.attributes.clear();
} }
this.proctoringGUIService.clear();
this.privileges = null; this.privileges = null;
if (isAvailable()) { if (isAvailable()) {

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2020 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.session;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.tomcat.util.buf.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.JavaScriptExecutor;
import org.slf4j.Logger;
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.exam.SEBProctoringConnectionData;
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.SEBClientsJoinProctorRoom;
public class ProctoringGUIService {
private static final Logger log = LoggerFactory.getLogger(ProctoringGUIService.class);
private static final String CLOSE_ROOM_SCRIPT = "var existingWin = window.open('', '%s'); existingWin.close()";
private final AtomicInteger counter = new AtomicInteger(1);
private final RestService restService;
final Set<String> openWindows = new HashSet<>();
final Map<String, RoomConnectionData> rooms = new HashMap<>();
public ProctoringGUIService(final RestService restService) {
this.restService = restService;
}
public String createNewRoomName() {
return "Room-" + this.counter.getAndIncrement();
}
public void registerProctoringWindow(final String window) {
this.openWindows.add(window);
}
public Set<String> roomNames() {
return this.rooms.keySet();
}
public void registerNewProcotringRoom(
final String examId,
final String roomName,
final Collection<String> connectionTokens) {
final List<SEBProctoringConnectionData> connections =
this.restService.getBuilder(SEBClientsJoinProctorRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
.withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, roomName)
.withFormParam(
API.EXAM_API_SEB_CONNECTION_TOKEN,
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR_CHAR))
.call()
.getOrThrow();
this.rooms.put(roomName, new RoomConnectionData(roomName, examId, connections));
this.openWindows.add(roomName);
}
public void addConnectionsToRoom(
final String examId,
final String room,
final Collection<String> connectionTokens) {
if (this.rooms.containsKey(room)) {
final RoomConnectionData roomConnectionData = this.rooms.get(room);
if (!roomConnectionData.examId.equals(examId) || !roomConnectionData.roomName.equals(room)) {
throw new IllegalArgumentException("Exam identifier mismatch");
}
final List<SEBProctoringConnectionData> newConnections =
this.restService.getBuilder(SEBClientsJoinProctorRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, examId)
.withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, room)
.withFormParam(
API.EXAM_API_SEB_CONNECTION_TOKEN,
StringUtils.join(connectionTokens, Constants.LIST_SEPARATOR_CHAR))
.call()
.getOrThrow();
roomConnectionData.connections.addAll(newConnections);
}
}
public void removeConnectionsFromRoom(
final String examId,
final String room,
final Collection<String> connectionTokens) {
}
public Result<List<SEBProctoringConnectionData>> closeRoom(final String name) {
return Result.tryCatch(() -> {
closeWindow(name);
final RoomConnectionData roomConnectionData = this.rooms.remove(name);
if (roomConnectionData != null) {
// first send instruction to leave this room and join the personal single room
final String connectionsString = StringUtils.join(
roomConnectionData.connections
.stream()
.map(c -> c.connectionToken)
.collect(Collectors.toList()),
Constants.LIST_SEPARATOR_CHAR);
// NOTE: uncomment this if we need to send first a LEAVE instruction before sending the JOIN instruction for the single room
// this.restService.getBuilder(LeaveProctorRoom.class)
// .withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
// .withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, name)
// .withFormParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionsString)
// .call()
// .getOrThrow();
return this.restService.getBuilder(SEBClientsJoinProctorRoom.class)
.withURIVariable(API.PARAM_MODEL_ID, roomConnectionData.examId)
.withFormParam(SEBProctoringConnectionData.ATTR_ROOM_NAME, name)
.withFormParam(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionsString)
.call()
.getOrThrow();
}
return Collections.emptyList();
});
}
public void clear() {
if (!this.rooms.isEmpty()) {
this.rooms.keySet().stream().forEach(this::closeRoom);
this.rooms.clear();
}
if (!this.openWindows.isEmpty()) {
this.openWindows.stream()
.forEach(room -> closeWindow(room));
this.openWindows.clear();
}
}
private void closeWindow(final String room) {
try {
RWT.getClient().getService(JavaScriptExecutor.class)
.execute(String.format(CLOSE_ROOM_SCRIPT, room));
} catch (final Exception e) {
log.info("Failed to close opened proctoring window: {}", room);
}
}
private static final class RoomConnectionData {
final String roomName;
final String examId;
final Collection<SEBProctoringConnectionData> connections;
protected RoomConnectionData(
final String roomName,
final String examId,
final Collection<SEBProctoringConnectionData> connections) {
this.roomName = roomName;
this.examId = examId;
this.connections = connections != null ? new ArrayList<>(connections) : new ArrayList<>();
}
}
}

View file

@ -10,8 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.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.ServerType; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
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;
public interface ExamProctoringService { public interface ExamProctoringService {
@ -20,19 +19,21 @@ public interface ExamProctoringService {
Result<Boolean> testExamProctoring(final ProctoringSettings examProctoring); Result<Boolean> testExamProctoring(final ProctoringSettings examProctoring);
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData( Result<SEBProctoringConnectionData> createProctorPrivateRoomConnection(
final ProctoringSettings examProctoring,
final String connectionToken);
Result<SEBProctoringConnectionData> createProctorPublicRoomConnection(
final ProctoringSettings examProctoring,
final String roomName);
Result<SEBProctoringConnectionData> createClientPrivateRoomConnection(
final ProctoringSettings examProctoring,
final String connectionToken);
Result<SEBProctoringConnectionData> createClientPublicRoomConnection(
final ProctoringSettings examProctoring, final ProctoringSettings examProctoring,
final String connectionToken, final String connectionToken,
final boolean server); final String roomName);
Result<SEBClientProctoringConnectionData> createProctoringConnectionData(
final ProctoringSettings examProctoring,
ClientConnection clientConnection,
boolean server);
Result<SEBClientProctoringConnectionData> createProcotringDataForRoom(
final ProctoringSettings examProctoring,
final String roomName,
final boolean server);
} }

View file

@ -25,8 +25,8 @@ import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
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; 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;
@ -72,64 +72,101 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
} }
@Override @Override
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData( public Result<SEBProctoringConnectionData> createProctorPrivateRoomConnection(
final ProctoringSettings examProctoring, final ProctoringSettings examProctoring,
final String connectionToken, final String connectionToken) {
final boolean server) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
return createProctoringConnectionData(
examProctoring,
this.examSessionService
.getConnectionData(connectionToken)
.getOrThrow().clientConnection,
server)
.getOrThrow();
});
}
@Override final ClientConnectionData clientConnection = this.examSessionService.getConnectionData(connectionToken)
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData( .getOrThrow();
final ProctoringSettings examProctoring,
final ClientConnection clientConnection,
final boolean server) {
return Result.tryCatch(() -> {
final long expTime = forExam(examProctoring); final long expTime = forExam(examProctoring);
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding(); final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
final String roomName = urlEncoder.encodeToString( final String roomName = urlEncoder.encodeToString(
Utils.toByteArray(clientConnection.connectionToken)); Utils.toByteArray(clientConnection.clientConnection.connectionToken));
return createProctoringConnectionData( return createProctoringConnectionData(
connectionToken,
examProctoring.serverURL, examProctoring.serverURL,
examProctoring.appKey, examProctoring.appKey,
examProctoring.getAppSecret(), examProctoring.getAppSecret(),
this.authorizationService.getUserService().getCurrentUser().getUsername(), this.authorizationService.getUserService().getCurrentUser().getUsername(),
(server) ? "seb-server" : "seb-client", "seb-server",
roomName, roomName,
clientConnection.userSessionId, clientConnection.clientConnection.userSessionId,
expTime) expTime)
.getOrThrow(); .getOrThrow();
}); });
} }
@Override @Override
public Result<SEBClientProctoringConnectionData> createProcotringDataForRoom( public Result<SEBProctoringConnectionData> createProctorPublicRoomConnection(
final ProctoringSettings examProctoring, final ProctoringSettings examProctoring,
final String roomName, final String roomName) {
final boolean server) {
return Result.tryCatch(() -> {
return createProctoringConnectionData(
null,
examProctoring.serverURL,
examProctoring.appKey,
examProctoring.getAppSecret(),
this.authorizationService.getUserService().getCurrentUser().getUsername(),
"seb-server",
roomName,
roomName,
forExam(examProctoring))
.getOrThrow();
});
}
@Override
public Result<SEBProctoringConnectionData> createClientPrivateRoomConnection(
final ProctoringSettings examProctoring,
final String connectionToken) {
// TODO Auto-generated method stub
return Result.tryCatch(() -> {
final ClientConnectionData clientConnection = this.examSessionService.getConnectionData(connectionToken)
.getOrThrow();
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
final String roomName = urlEncoder.encodeToString(
Utils.toByteArray(clientConnection.clientConnection.connectionToken));
return createProctoringConnectionData(
null,
examProctoring.serverURL,
examProctoring.appKey,
examProctoring.getAppSecret(),
clientConnection.clientConnection.userSessionId,
"seb-server",
roomName,
clientConnection.clientConnection.userSessionId,
forExam(examProctoring))
.getOrThrow();
});
}
@Override
public Result<SEBProctoringConnectionData> createClientPublicRoomConnection(
final ProctoringSettings examProctoring,
final String connectionToken,
final String roomName) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
final long expTime = forExam(examProctoring); final long expTime = forExam(examProctoring);
final ClientConnectionData connectionData = this.examSessionService.getConnectionData(connectionToken)
.getOrThrow();
return createProctoringConnectionData( return createProctoringConnectionData(
connectionToken,
examProctoring.serverURL, examProctoring.serverURL,
examProctoring.appKey, examProctoring.appKey,
examProctoring.getAppSecret(), examProctoring.getAppSecret(),
this.authorizationService.getUserService().getCurrentUser().getUsername(), connectionData.clientConnection.userSessionId,
(server) ? "seb-server" : "seb-client", "seb-client",
roomName, roomName,
roomName, roomName,
expTime) expTime)
@ -138,7 +175,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
} }
public Result<SEBClientProctoringConnectionData> createProctoringConnectionData( public Result<SEBProctoringConnectionData> createProctoringConnectionData(
final String connectionToken,
final String url, final String url,
final String appKey, final String appKey,
final CharSequence appSecret, final CharSequence appSecret,
@ -165,18 +203,13 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
expTime, expTime,
host); host);
final StringBuilder builder = new StringBuilder(); return new SEBProctoringConnectionData(
final String connectionURL = builder.append(roomUrl) connectionToken,
.append("?jwt=")
.append(token).toString();
return new SEBClientProctoringConnectionData(
host, host,
roomUrl, roomUrl,
roomName, roomName,
subject, subject,
token, token);
connectionURL);
}); });
} }

View file

@ -28,7 +28,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
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.ServerType; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings.ServerType;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
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.ClientConnection.ConnectionStatus; 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.ClientConnectionData;
@ -360,12 +360,11 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
.getExamProctoring(clientConnection.examId) .getExamProctoring(clientConnection.examId)
.getOrThrow(); .getOrThrow();
final SEBClientProctoringConnectionData proctoringData = final SEBProctoringConnectionData proctoringData =
this.examAdminService.getExamProctoringService(proctoringSettings.serverType) this.examAdminService.getExamProctoringService(proctoringSettings.serverType)
.flatMap(s -> s.createProctoringConnectionData( .flatMap(s -> s.createClientPrivateRoomConnection(
proctoringSettings, proctoringSettings,
clientConnection.connectionToken, clientConnection.connectionToken))
false))
.getOrThrow(); .getOrThrow();
final Map<String, String> attributes = new HashMap<>(); final Map<String, String> attributes = new HashMap<>();

View file

@ -234,6 +234,9 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
} }
private ClientInstructionRecord chacheInstruction(final ClientInstructionRecord instruction) { private ClientInstructionRecord chacheInstruction(final ClientInstructionRecord instruction) {
System.out.println("************* register instruction: " + instruction);
final String connectionToken = instruction.getConnectionToken(); final String connectionToken = instruction.getConnectionToken();
if (this.instructions.containsKey(connectionToken)) { if (this.instructions.containsKey(connectionToken)) {
// check if previous instruction is still valid // check if previous instruction is still valid

View file

@ -11,11 +11,13 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid; import javax.validation.Valid;
@ -43,7 +45,7 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Page; import ch.ethz.seb.sebserver.gbl.model.Page;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus; 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.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction; import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
@ -281,32 +283,49 @@ public class ExamMonitoringController {
} }
//***********************************************************************************************
//**** Proctoring
@RequestMapping( @RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.PROCTOR_PATH_SEGMENT + API.PROCTOR_PATH_SEGMENT,
+ API.EXAM_MONITORING_SEB_CONNECTION_TOKEN_PATH_SEGMENT,
method = RequestMethod.GET, method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public SEBClientProctoringConnectionData getClientSingleRoomProctoringData( public SEBProctoringConnectionData getProctorSingleRoom(
@RequestParam( @RequestParam(
name = API.PARAM_INSTITUTION_ID, name = API.PARAM_INSTITUTION_ID,
required = true, required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId, @PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
@PathVariable(name = API.EXAM_API_SEB_CONNECTION_TOKEN) final String connectionToken) { @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = false) final String connectionToken,
@RequestParam(name = SEBProctoringConnectionData.ATTR_ROOM_NAME, required = false) final String roomName) {
this.authorization.check( this.authorization.check(
PrivilegeType.READ, PrivilegeType.READ,
EntityType.EXAM, EntityType.EXAM,
institutionId); institutionId);
return this.examSessionService.getRunningExam(examId) if (StringUtils.isNotBlank(connectionToken)) {
.flatMap(this.authorization::checkRead) return this.examSessionService.getRunningExam(examId)
.flatMap(this.examAdminService::getExamProctoring) .flatMap(this.authorization::checkRead)
.flatMap(proc -> this.examAdminService .flatMap(this.examAdminService::getExamProctoring)
.getExamProctoringService(proc.serverType) .flatMap(proc -> this.examAdminService
.flatMap(s -> s.createProctoringConnectionData(proc, connectionToken, true))) .getExamProctoringService(proc.serverType)
.getOrThrow(); .flatMap(s -> s.createProctorPrivateRoomConnection(proc, connectionToken)))
.getOrThrow();
} else if (StringUtils.isNotBlank(roomName)) {
return this.examSessionService.getRunningExam(examId)
.flatMap(this.authorization::checkRead)
.flatMap(this.examAdminService::getExamProctoring)
.flatMap(proc -> this.examAdminService
.getExamProctoringService(proc.serverType)
.flatMap(s -> s.createProctorPublicRoomConnection(proc, roomName)))
.getOrThrow();
} else {
return null;
}
} }
@RequestMapping( @RequestMapping(
@ -315,62 +334,14 @@ public class ExamMonitoringController {
+ API.PROCTOR_JOIN_ROOM_PATH_SEGMENT, + API.PROCTOR_JOIN_ROOM_PATH_SEGMENT,
method = RequestMethod.POST, method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE) produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public SEBClientProctoringConnectionData joinProctoringRoom( public List<SEBProctoringConnectionData> joinProctoringRoom(
@RequestParam( @RequestParam(
name = API.PARAM_INSTITUTION_ID, name = API.PARAM_INSTITUTION_ID,
required = true, required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId, defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_MODEL_ID) final Long examId, @PathVariable(name = API.PARAM_MODEL_ID) final Long examId,
@RequestParam( @RequestParam(
name = SEBClientProctoringConnectionData.ATTR_ROOM_NAME, name = SEBProctoringConnectionData.ATTR_ROOM_NAME,
required = true) final String roomName,
@RequestParam(
name = API.EXAM_API_SEB_CONNECTION_TOKEN,
required = true) final String connectionTokens) {
this.authorization.check(
PrivilegeType.READ,
EntityType.EXAM,
institutionId);
final ProctoringSettings settings = this.examSessionService
.getRunningExam(examId)
.flatMap(this.authorization::checkRead)
.flatMap(this.examAdminService::getExamProctoring)
.getOrThrow();
final SEBClientProctoringConnectionData result = this.examAdminService
.getExamProctoringService(settings.serverType)
.flatMap(s -> s.createProcotringDataForRoom(settings, roomName, false))
.getOrThrow();
if (StringUtils.isNotBlank(connectionTokens)) {
(connectionTokens.contains(Constants.LIST_SEPARATOR)
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
: Arrays.asList(connectionTokens)).stream()
.forEach(connectionToken -> sendJoinInstruction(examId, connectionToken, result)
.onError(error -> log.error(
"Failed to send proctoring leave instruction to client: {} ",
connectionToken, error)));
}
return result;
}
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.PROCTOR_PATH_SEGMENT
+ API.PROCTOR_LEAVE_ROOM_PATH_SEGMENT,
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public void leaveProctoringRoom(
@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 = SEBClientProctoringConnectionData.ATTR_ROOM_NAME,
required = true) final String roomName, required = true) final String roomName,
@RequestParam( @RequestParam(
name = API.EXAM_API_SEB_CONNECTION_TOKEN, name = API.EXAM_API_SEB_CONNECTION_TOKEN,
@ -391,17 +362,85 @@ public class ExamMonitoringController {
.getExamProctoringService(settings.serverType) .getExamProctoringService(settings.serverType)
.getOrThrow(); .getOrThrow();
(connectionTokens.contains(Constants.LIST_SEPARATOR) if (StringUtils.isNotBlank(connectionTokens)) {
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR)) return (connectionTokens.contains(Constants.LIST_SEPARATOR)
: Arrays.asList(connectionTokens)).stream() ? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
.forEach(connectionToken -> examProctoringService : Arrays.asList(connectionTokens))
.createProctoringConnectionData(settings, connectionToken, false) .stream()
.flatMap(data -> sendLeaveInstruction(examId, connectionToken, data)) .map(connectionToken -> {
.onError(error -> log.error( final SEBProctoringConnectionData data = examProctoringService
"Failed to send proctoring leave instruction to client: {} ", .createClientPublicRoomConnection(settings, connectionToken, roomName)
connectionToken, error))); .getOrThrow();
sendJoinInstruction(examId, connectionToken, data)
.onError(error -> log.error(
"Failed to send proctoring leave instruction to client: {} ",
connectionToken, error));
return data;
}).collect(Collectors.toList());
}
return Collections.emptyList();
} }
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.PROCTOR_PATH_SEGMENT
+ API.PROCTOR_LEAVE_ROOM_PATH_SEGMENT,
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public List<SEBProctoringConnectionData> leaveProctoringRoom(
@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 = API.EXAM_API_SEB_CONNECTION_TOKEN,
required = true) final String connectionTokens) {
this.authorization.check(
PrivilegeType.READ,
EntityType.EXAM,
institutionId);
final ProctoringSettings settings = this.examSessionService
.getRunningExam(examId)
.flatMap(this.authorization::checkRead)
.flatMap(this.examAdminService::getExamProctoring)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
.getExamProctoringService(settings.serverType)
.getOrThrow();
if (StringUtils.isNotBlank(connectionTokens)) {
return (connectionTokens.contains(Constants.LIST_SEPARATOR)
? Arrays.asList(StringUtils.split(connectionTokens, Constants.LIST_SEPARATOR))
: Arrays.asList(connectionTokens))
.stream()
.map(connectionToken -> {
final SEBProctoringConnectionData data = examProctoringService
.createClientPublicRoomConnection(settings, connectionToken, roomName)
.getOrThrow();
sendLeaveInstruction(examId, connectionTokens, data)
.onError(error -> log.error(
"Failed to send proctoring leave instruction for common room to client: {} ",
connectionToken, error));
return data;
}).collect(Collectors.toList());
}
return Collections.emptyList();
}
//**** Proctoring
//***********************************************************************************************
private boolean hasRunningExamPrivilege(final Long examId, final Long institution) { private boolean hasRunningExamPrivilege(final Long examId, final Long institution) {
return hasRunningExamPrivilege( return hasRunningExamPrivilege(
this.examSessionService.getRunningExam(examId).getOr(null), this.examSessionService.getRunningExam(examId).getOr(null),
@ -420,7 +459,7 @@ public class ExamMonitoringController {
private Result<Void> sendJoinInstruction( private Result<Void> sendJoinInstruction(
final Long examId, final Long examId,
final String connectionToken, final String connectionToken,
final SEBClientProctoringConnectionData data) { final SEBProctoringConnectionData data) {
return sendProctorInstruction( return sendProctorInstruction(
examId, examId,
@ -432,7 +471,7 @@ public class ExamMonitoringController {
private Result<Void> sendLeaveInstruction( private Result<Void> sendLeaveInstruction(
final Long examId, final Long examId,
final String connectionToken, final String connectionToken,
final SEBClientProctoringConnectionData data) { final SEBProctoringConnectionData data) {
return sendProctorInstruction( return sendProctorInstruction(
examId, examId,
@ -444,7 +483,7 @@ public class ExamMonitoringController {
private Result<Void> sendProctorInstruction( private Result<Void> sendProctorInstruction(
final Long examId, final Long examId,
final String connectionToken, final String connectionToken,
final SEBClientProctoringConnectionData data, final SEBProctoringConnectionData data,
final String method) { final String method) {
final Map<String, String> attributes = new HashMap<>(); final Map<String, String> attributes = new HashMap<>();
attributes.put( attributes.put(

View file

@ -1428,6 +1428,11 @@ sebserver.monitoring.exam.list.title=Running Exams
sebserver.monitoring.exam.list.actions= sebserver.monitoring.exam.list.actions=
sebserver.monitoring.exam.action.detail.view=Back To Monitoring sebserver.monitoring.exam.action.detail.view=Back To Monitoring
sebserver.monitoring.exam.action.list.view=Monitoring sebserver.monitoring.exam.action.list.view=Monitoring
sebserver.monitoring.exam.action.newroom=New Proctor Room
sebserver.monitoring.exam.action.viewroom=View Room {0}
sebserver.monitoring.exam.action.closeroom=Close Room {0}
sebserver.exam.monitoring.action.category.filter=Filter
sebserver.exam.overall.action.category.proctoring=Proctoring
sebserver.monitoring.exam.info.pleaseSelect=At first please select an Exam from the list sebserver.monitoring.exam.info.pleaseSelect=At first please select an Exam from the list

View file

@ -24,7 +24,7 @@ public class CurrentUserTest extends GuiIntegrationTest {
public void testCurrentUserLoginAndGet() { public void testCurrentUserLoginAndGet() {
final OAuth2AuthorizationContextHolder authorizationContextHolder = getAuthorizationContextHolder(); final OAuth2AuthorizationContextHolder authorizationContextHolder = getAuthorizationContextHolder();
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder); final CurrentUser currentUser = new CurrentUser(authorizationContextHolder, null);
// no user is logged in for now // no user is logged in for now
try { try {
@ -51,7 +51,7 @@ public class CurrentUserTest extends GuiIntegrationTest {
public void testCurrentUserPrivileges() { public void testCurrentUserPrivileges() {
final OAuth2AuthorizationContextHolder authorizationContextHolder = getAuthorizationContextHolder(); final OAuth2AuthorizationContextHolder authorizationContextHolder = getAuthorizationContextHolder();
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder); final CurrentUser currentUser = new CurrentUser(authorizationContextHolder, null);
// login as SEB Administrator // login as SEB Administrator
authorizationContextHolder.getAuthorizationContext().login("admin", "admin"); authorizationContextHolder.getAuthorizationContext().login("admin", "admin");
@ -64,7 +64,7 @@ public class CurrentUserTest extends GuiIntegrationTest {
@Test @Test
public void testCurrentUserLogin() { public void testCurrentUserLogin() {
final OAuth2AuthorizationContextHolder authorizationContextHolder = login("admin", "admin"); final OAuth2AuthorizationContextHolder authorizationContextHolder = login("admin", "admin");
final CurrentUser currentUser = new CurrentUser(authorizationContextHolder); final CurrentUser currentUser = new CurrentUser(authorizationContextHolder, null);
final UserInfo userInfo = currentUser.getOrHandleError(error -> { final UserInfo userInfo = currentUser.getOrHandleError(error -> {
fail("expecting no error here"); fail("expecting no error here");
return null; return null;

View file

@ -14,7 +14,7 @@ import static org.junit.Assert.assertNotNull;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData; import ch.ethz.seb.sebserver.gbl.model.exam.SEBProctoringConnectionData;
import ch.ethz.seb.sebserver.gbl.util.Cryptor; import ch.ethz.seb.sebserver.gbl.util.Cryptor;
public class ExamJITSIProctoringServiceTest { public class ExamJITSIProctoringServiceTest {
@ -25,7 +25,8 @@ public class ExamJITSIProctoringServiceTest {
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, cryptorMock); new ExamJITSIProctoringService(null, null, cryptorMock);
final SEBClientProctoringConnectionData data = examJITSIProctoringService.createProctoringConnectionData( final SEBProctoringConnectionData data = examJITSIProctoringService.createProctoringConnectionData(
"connectionToken",
"https://seb-jitsi.example.ch", "https://seb-jitsi.example.ch",
"test-app", "test-app",
"fbvgeghergrgrthrehreg123", "fbvgeghergrgrthrehreg123",
@ -43,9 +44,6 @@ public class ExamJITSIProctoringServiceTest {
assertEquals( assertEquals(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4",
data.accessToken); data.accessToken);
assertEquals(
"https://seb-jitsi.example.ch/SomeRoom?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4",
data.connectionURL);
} }