SEBSERV-435 implements screen proctoring service linking
This commit is contained in:
parent
a04c111b59
commit
cf1a84a38d
10 changed files with 247 additions and 130 deletions
|
@ -8,7 +8,11 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gbl.model.exam;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -18,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
|
@ -177,6 +182,64 @@ public class ProctoringServiceSettings implements Entity {
|
|||
this.useZoomAppClientForCollectingRoom = copyOf.useZoomAppClientForCollectingRoom;
|
||||
}
|
||||
|
||||
public ProctoringServiceSettings(final Exam exam) {
|
||||
if (exam == null) {
|
||||
throw new IllegalStateException("Exam has null reference");
|
||||
}
|
||||
if (!exam.additionalAttributesIncluded()) {
|
||||
throw new IllegalStateException("Exam has no additional attributes");
|
||||
}
|
||||
|
||||
this.examId = exam.id;
|
||||
this.enableProctoring = BooleanUtils.toBooleanObject(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_ENABLE_PROCTORING,
|
||||
Constants.FALSE_STRING));
|
||||
this.serverType = ProctoringServerType.valueOf(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_SERVER_TYPE,
|
||||
ProctoringServerType.ZOOM.name()));
|
||||
this.serverURL = exam.additionalAttributes.get(ATTR_SERVER_URL);
|
||||
this.collectingRoomSize = Integer.parseInt(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_COLLECTING_ROOM_SIZE,
|
||||
"-1"));
|
||||
this.enabledFeatures = getEnabledFeatures(exam.additionalAttributes);
|
||||
this.serviceInUse = this.enableProctoring;
|
||||
this.appKey = exam.additionalAttributes.get(ATTR_APP_KEY);
|
||||
this.appSecret = exam.additionalAttributes.get(ATTR_APP_SECRET);
|
||||
this.accountId = exam.additionalAttributes.get(ATTR_ACCOUNT_ID);
|
||||
this.clientId = exam.additionalAttributes.get(ATTR_ACCOUNT_CLIENT_ID);
|
||||
this.clientSecret = exam.additionalAttributes.get(ATTR_ACCOUNT_CLIENT_SECRET);
|
||||
this.sdkKey = exam.additionalAttributes.get(ATTR_SDK_KEY);
|
||||
this.sdkSecret = exam.additionalAttributes.get(ATTR_SDK_SECRET);
|
||||
this.useZoomAppClientForCollectingRoom = BooleanUtils.toBooleanObject(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
|
||||
Constants.FALSE_STRING));
|
||||
}
|
||||
|
||||
private EnumSet<ProctoringFeature> getEnabledFeatures(final Map<String, String> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLED_FEATURES)) {
|
||||
try {
|
||||
final String value = mapping.get(ProctoringServiceSettings.ATTR_ENABLED_FEATURES);
|
||||
return StringUtils.isNotBlank(value)
|
||||
? EnumSet.copyOf(Arrays.asList(StringUtils.split(value, Constants.LIST_SEPARATOR))
|
||||
.stream()
|
||||
.map(str -> {
|
||||
try {
|
||||
return ProctoringFeature.valueOf(str);
|
||||
} catch (final Exception e) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet()))
|
||||
: EnumSet.noneOf(ProctoringFeature.class);
|
||||
} catch (final Exception e) {
|
||||
return EnumSet.allOf(ProctoringFeature.class);
|
||||
}
|
||||
} else {
|
||||
return EnumSet.allOf(ProctoringFeature.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelId() {
|
||||
return (this.examId != null) ? String.valueOf(this.examId) : null;
|
||||
|
|
|
@ -10,12 +10,14 @@ package ch.ethz.seb.sebserver.gbl.model.exam;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
@ -83,6 +85,31 @@ public class ScreenProctoringSettings {
|
|||
this.collectingGroupSize = collectingGroupSize;
|
||||
}
|
||||
|
||||
public ScreenProctoringSettings(final Exam exam) {
|
||||
if (exam == null) {
|
||||
throw new IllegalStateException("Exam has null reference");
|
||||
}
|
||||
if (!exam.additionalAttributesIncluded()) {
|
||||
throw new IllegalStateException("Exam has no additional attributes");
|
||||
}
|
||||
|
||||
this.examId = exam.id;
|
||||
this.enableScreenProctoring = BooleanUtils.toBooleanObject(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_ENABLE_SCREEN_PROCTORING,
|
||||
Constants.FALSE_STRING));
|
||||
this.spsServiceURL = exam.additionalAttributes.get(ATTR_SPS_SERVICE_URL);
|
||||
this.spsAPIKey = exam.additionalAttributes.get(ATTR_SPS_API_KEY);
|
||||
this.spsAPISecret = exam.additionalAttributes.get(ATTR_SPS_API_SECRET);
|
||||
this.spsAccountId = exam.additionalAttributes.get(ATTR_SPS_ACCOUNT_ID);
|
||||
this.spsAccountPassword = exam.additionalAttributes.get(ATTR_SPS_ACCOUNT_PASSWORD);
|
||||
this.collectingStrategy = CollectingStrategy.valueOf(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_COLLECTING_STRATEGY,
|
||||
CollectingStrategy.EXAM.name()));
|
||||
this.collectingGroupSize = Integer.parseInt(exam.additionalAttributes.getOrDefault(
|
||||
ATTR_COLLECTING_GROUP_SIZE,
|
||||
"-1"));
|
||||
}
|
||||
|
||||
public Long getExamId() {
|
||||
return this.examId;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
@ -25,17 +26,21 @@ import org.springframework.stereotype.Component;
|
|||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
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.SEBServerAuthorizationContext;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ScreenProctoringWindowData;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringWindowScriptResolver;
|
||||
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ProctoringServlet extends HttpServlet {
|
||||
|
||||
public static final String SCREEN_PROCOTRING_FLAG_PARAM = "screenproctoring";
|
||||
|
||||
private static final long serialVersionUID = 3475978419653411800L;
|
||||
private static final Logger log = LoggerFactory.getLogger(ProctoringServlet.class);
|
||||
|
||||
|
@ -58,12 +63,54 @@ public class ProctoringServlet extends HttpServlet {
|
|||
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(servletContext);
|
||||
|
||||
final boolean authenticated = isAuthenticated(httpSession, webApplicationContext);
|
||||
if (!authenticated) {
|
||||
UserInfo user;
|
||||
try {
|
||||
user = isAuthenticated(httpSession, webApplicationContext);
|
||||
} catch (final Exception e) {
|
||||
resp.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
return;
|
||||
}
|
||||
|
||||
final String parameter = req.getParameter(SCREEN_PROCOTRING_FLAG_PARAM);
|
||||
if (BooleanUtils.toBoolean(parameter)) {
|
||||
openScreenProctoring(req, resp, user, httpSession);
|
||||
} else {
|
||||
openRemoteProctoring(resp, httpSession);
|
||||
}
|
||||
}
|
||||
|
||||
private void openScreenProctoring(
|
||||
final HttpServletRequest req,
|
||||
final HttpServletResponse resp,
|
||||
final UserInfo user,
|
||||
final HttpSession httpSession) throws IOException {
|
||||
|
||||
final ScreenProctoringWindowData data = (ScreenProctoringWindowData) httpSession
|
||||
.getAttribute(ProctoringGUIService.SESSION_ATTR_SCREEN_PROCTORING_DATA);
|
||||
|
||||
// NOTE: POST on data.loginLocation seems not to work for automated login
|
||||
// TODO discuss with Nadim how to make a direct login POST on the GUI client
|
||||
// maybe there is a way to expose /login endpoint for directly POST credentials for login.
|
||||
|
||||
// https://stackoverflow.com/questions/46582/response-redirect-with-post-instead-of-get
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("<html>");
|
||||
sb.append("<body onload='document.forms[\"form\"].submit()'>");
|
||||
sb.append("<form name='form' action='");
|
||||
sb.append(data.loginLocation).append("' method='post'>");
|
||||
sb.append("</input type='hidden' name='username' value='").append("super-admin").append("'>");
|
||||
sb.append("</input type='hidden' name='password' type='password' value='").append("admin").append("'>");
|
||||
sb.append("</form>");
|
||||
sb.append("</body>");
|
||||
sb.append("</html>");
|
||||
|
||||
resp.getOutputStream().println(sb.toString());
|
||||
}
|
||||
|
||||
private void openRemoteProctoring(
|
||||
final HttpServletResponse resp,
|
||||
final HttpSession httpSession) throws IOException {
|
||||
|
||||
final ProctoringWindowData proctoringData =
|
||||
(ProctoringWindowData) httpSession
|
||||
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
|
||||
|
@ -89,7 +136,7 @@ public class ProctoringServlet extends HttpServlet {
|
|||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private boolean isAuthenticated(
|
||||
private UserInfo isAuthenticated(
|
||||
final HttpSession httpSession,
|
||||
final WebApplicationContext webApplicationContext) {
|
||||
|
||||
|
@ -97,7 +144,11 @@ public class ProctoringServlet extends HttpServlet {
|
|||
.getBean(AuthorizationContextHolder.class);
|
||||
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
||||
.getAuthorizationContext(httpSession);
|
||||
return authorizationContext.isValid() && authorizationContext.isLoggedIn();
|
||||
if (!authorizationContext.isValid() || !authorizationContext.isLoggedIn()) {
|
||||
throw new RuntimeException("No authentication found");
|
||||
}
|
||||
|
||||
return authorizationContext.getLoggedInUser().getOrThrow();
|
||||
}
|
||||
|
||||
}
|
|
@ -45,9 +45,6 @@ public class RAPSpringConfig {
|
|||
@Value("${sebserver.gui.remote.proctoring.api-servler.endpoint:/remote-view-servlet}")
|
||||
private String remoteProctoringViewServletEndpoint;
|
||||
|
||||
@Value("${sebserver.gui.screen.proctoring.api-servler.endpoint:/screen-proctoring}")
|
||||
private String screenProctoringViewServletEndpoint;
|
||||
|
||||
@Bean
|
||||
public StaticApplicationPropertyResolver staticApplicationPropertyResolver() {
|
||||
return new StaticApplicationPropertyResolver();
|
||||
|
@ -86,17 +83,6 @@ public class RAPSpringConfig {
|
|||
this.remoteProctoringEndpoint + this.remoteProctoringViewServletEndpoint + "/*");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<ScreenProctoringServlet> servletScreenProctoringRegistrationBean(
|
||||
final ApplicationContext applicationContext) {
|
||||
|
||||
final ScreenProctoringServlet proctoringServlet = applicationContext
|
||||
.getBean(ScreenProctoringServlet.class);
|
||||
return new ServletRegistrationBean<>(
|
||||
proctoringServlet,
|
||||
this.remoteProctoringEndpoint + this.screenProctoringViewServletEndpoint + "/*");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageSource messageSource() {
|
||||
final ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource =
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||
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.SEBServerAuthorizationContext;
|
||||
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ScreenProctoringServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 4147410676185956971L;
|
||||
|
||||
@Override
|
||||
protected void doGet(
|
||||
final HttpServletRequest req,
|
||||
final HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
final HttpSession httpSession = req.getSession();
|
||||
final ServletContext servletContext = httpSession.getServletContext();
|
||||
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(servletContext);
|
||||
|
||||
final UserInfo user = isAuthenticated(httpSession, webApplicationContext);
|
||||
|
||||
// TODO https://stackoverflow.com/questions/46582/response-redirect-with-post-instead-of-get
|
||||
|
||||
final String hello = "Hello";
|
||||
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.Append("<html>");
|
||||
// sb.AppendFormat(@"<body onload='document.forms[""form""].submit()'>");
|
||||
// sb.AppendFormat("<form name='form' action='{0}' method='post'>",postbackUrl);
|
||||
// sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", id);
|
||||
// // Other params go here
|
||||
// sb.Append("</form>");
|
||||
// sb.Append("</body>");
|
||||
// sb.Append("</html>");
|
||||
|
||||
resp.getOutputStream().println(hello);
|
||||
|
||||
}
|
||||
|
||||
private UserInfo isAuthenticated(
|
||||
final HttpSession httpSession,
|
||||
final WebApplicationContext webApplicationContext) {
|
||||
|
||||
final AuthorizationContextHolder authorizationContextHolder = webApplicationContext
|
||||
.getBean(AuthorizationContextHolder.class);
|
||||
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
||||
.getAuthorizationContext(httpSession);
|
||||
if (!authorizationContext.isValid() || !authorizationContext.isLoggedIn()) {
|
||||
throw new RuntimeException("No authentication found");
|
||||
}
|
||||
|
||||
return authorizationContext.getLoggedInUser().getOrThrow();
|
||||
}
|
||||
|
||||
}
|
|
@ -65,8 +65,6 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
|||
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
|
||||
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.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetScreenProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.GetClientGroups;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRooms;
|
||||
|
@ -279,18 +277,8 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
clientTable,
|
||||
isExamSupporter));
|
||||
|
||||
final ProctoringServiceSettings proctoringSettings = this.restService
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOr(null);
|
||||
|
||||
final ScreenProctoringSettings screenProctoringSettings = this.restService
|
||||
.getBuilder(GetScreenProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOr(null);
|
||||
|
||||
final ProctoringServiceSettings proctoringSettings = new ProctoringServiceSettings(exam);
|
||||
final ScreenProctoringSettings screenProctoringSettings = new ScreenProctoringSettings(exam);
|
||||
guiUpdates.add(createProctoringActions(
|
||||
proctoringSettings,
|
||||
screenProctoringSettings,
|
||||
|
@ -330,7 +318,12 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
final boolean proctoringEnabled = proctoringSettings != null &&
|
||||
BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
|
||||
final boolean screenProctoringEnabled = screenProctoringSettings != null &&
|
||||
BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
|
||||
BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring);
|
||||
|
||||
if (!proctoringEnabled && !screenProctoringEnabled) {
|
||||
return monitoringStatus -> {
|
||||
};
|
||||
}
|
||||
|
||||
if (proctoringEnabled && proctoringSettings.enabledFeatures.contains(ProctoringFeature.TOWN_HALL)) {
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
|
@ -361,7 +354,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
}
|
||||
}
|
||||
|
||||
proctoringGUIService.clearCollectingRoomActionState();
|
||||
proctoringGUIService.clearActionState();
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
final Collection<RemoteProctoringRoom> collectingRooms = (proctoringEnabled)
|
||||
? this.pageService
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
@ -51,6 +50,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
|||
import ch.ethz.seb.sebserver.gbl.util.Tuple;
|
||||
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.ActionPane;
|
||||
import ch.ethz.seb.sebserver.gui.content.monitoring.ProctorRoomConnectionsPopup;
|
||||
|
@ -64,6 +64,7 @@ import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetProctorRoomConnection;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.IsTownhallRoomAvailable;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.NotifyProctoringRoomOpened;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
@ -97,9 +98,7 @@ public class MonitoringProctoringService {
|
|||
private final JSONMapper jsonMapper;
|
||||
private final Resource openRoomScriptRes;
|
||||
private final String remoteProctoringEndpoint;
|
||||
|
||||
@Value("${sebserver.gui.screen.proctoring.api-servler.endpoint:/screen-proctoring}")
|
||||
private String screenProctoringViewServletEndpoint;
|
||||
private final String remoteProctoringViewServletEndpoint;
|
||||
|
||||
public MonitoringProctoringService(
|
||||
final PageService pageService,
|
||||
|
@ -107,7 +106,8 @@ public class MonitoringProctoringService {
|
|||
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
||||
final JSONMapper jsonMapper,
|
||||
@Value(OPEN_ROOM_SCRIPT_RES) final Resource openRoomScript,
|
||||
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint) {
|
||||
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint,
|
||||
@Value("${sebserver.gui.remote.proctoring.api-servler.endpoint:/remote-view-servlet}") final String remoteProctoringViewServletEndpoint) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.guiServiceInfo = guiServiceInfo;
|
||||
|
@ -115,6 +115,7 @@ public class MonitoringProctoringService {
|
|||
this.jsonMapper = jsonMapper;
|
||||
this.openRoomScriptRes = openRoomScript;
|
||||
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
||||
this.remoteProctoringViewServletEndpoint = remoteProctoringViewServletEndpoint;
|
||||
}
|
||||
|
||||
public boolean isTownhallRoomActive(final String examModelId) {
|
||||
|
@ -313,19 +314,33 @@ public class MonitoringProctoringService {
|
|||
final ScreenProctoringGroup group,
|
||||
final PageAction _action) {
|
||||
|
||||
final String serviceRedirect = settings.spsServiceURL + "/guilogin";
|
||||
final ResponseEntity<Void> redirect = new RestTemplate().exchange(
|
||||
// TODO make this configurable or static
|
||||
final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location";
|
||||
final ResponseEntity<String> redirect = new RestTemplate().exchange(
|
||||
serviceRedirect,
|
||||
HttpMethod.GET,
|
||||
null,
|
||||
Void.class);
|
||||
String.class);
|
||||
|
||||
final String redirectLocation = redirect.getBody();
|
||||
final CurrentUser currentUser = this.pageService.getCurrentUser();
|
||||
|
||||
ProctoringGUIService.setCurrentScreenProctoringWindowData(
|
||||
group.uuid,
|
||||
redirectLocation,
|
||||
currentUser.get().username,
|
||||
"admin");
|
||||
|
||||
final URI redirectLocation = redirect.getHeaders().getLocation();
|
||||
final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
|
||||
final String url = this.remoteProctoringEndpoint
|
||||
+ this.screenProctoringViewServletEndpoint
|
||||
+ "?group=" + group.uuid
|
||||
+ "&loc=" + redirectLocation;
|
||||
final String url = this.guiServiceInfo.getExternalServerURIBuilder().toUriString()
|
||||
+ this.remoteProctoringEndpoint
|
||||
+ this.remoteProctoringViewServletEndpoint
|
||||
+ Constants.SLASH
|
||||
+ Constants.QUERY
|
||||
+ ProctoringServlet.SCREEN_PROCOTRING_FLAG_PARAM
|
||||
+ Constants.EQUALITY_SIGN
|
||||
+ Constants.TRUE_STRING;
|
||||
|
||||
launcher.openURL(url);
|
||||
return _action;
|
||||
}
|
||||
|
|
|
@ -129,8 +129,9 @@ public class ProctoringGUIService {
|
|||
.reduce(0, (acc, room) -> acc + room.a.roomSize, Integer::sum);
|
||||
}
|
||||
|
||||
public void clearCollectingRoomActionState() {
|
||||
public void clearActionState() {
|
||||
this.collectingRoomsActionState.clear();
|
||||
this.screenProctoringGroupState.clear();
|
||||
}
|
||||
|
||||
public boolean registerProctoringWindow(
|
||||
|
@ -157,12 +158,28 @@ public class ProctoringGUIService {
|
|||
.getAttribute(SESSION_ATTR_PROCTORING_DATA);
|
||||
}
|
||||
|
||||
public static ScreenProctoringWindowData getCurrentScreemProctoringWindowData() {
|
||||
return (ScreenProctoringWindowData) RWT.getUISession()
|
||||
.getHttpSession()
|
||||
.getAttribute(SESSION_ATTR_SCREEN_PROCTORING_DATA);
|
||||
}
|
||||
|
||||
public static void setCurrentProctoringWindowData(
|
||||
final String examId,
|
||||
final ProctoringRoomConnection data) {
|
||||
setCurrentProctoringWindowData(examId, data.roomName, data);
|
||||
}
|
||||
|
||||
public static void setCurrentScreenProctoringWindowData(
|
||||
final String groupId,
|
||||
final String loginLocation,
|
||||
final String username,
|
||||
final String password) {
|
||||
RWT.getUISession().getHttpSession().setAttribute(
|
||||
SESSION_ATTR_SCREEN_PROCTORING_DATA,
|
||||
new ScreenProctoringWindowData(groupId, loginLocation, username, password));
|
||||
}
|
||||
|
||||
public static void setCurrentProctoringWindowData(
|
||||
final String examId,
|
||||
final String windowName,
|
||||
|
@ -226,6 +243,7 @@ public class ProctoringGUIService {
|
|||
|
||||
public void clear() {
|
||||
this.collectingRoomsActionState.clear();
|
||||
this.screenProctoringGroupState.clear();
|
||||
if (!this.openWindows.isEmpty()) {
|
||||
new HashSet<>(this.openWindows.entrySet())
|
||||
.stream()
|
||||
|
@ -265,6 +283,8 @@ public class ProctoringGUIService {
|
|||
|
||||
public static class ProctoringWindowData implements Serializable {
|
||||
private static final long serialVersionUID = -9060185011534956417L;
|
||||
|
||||
public final boolean isScreenProctoring;
|
||||
public final String windowName;
|
||||
public final String examId;
|
||||
public final ProctoringRoomConnection connectionData;
|
||||
|
@ -273,10 +293,29 @@ public class ProctoringGUIService {
|
|||
final String examId,
|
||||
final String windowName,
|
||||
final ProctoringRoomConnection connectionData) {
|
||||
this.isScreenProctoring = false;
|
||||
this.windowName = windowName;
|
||||
this.examId = examId;
|
||||
this.connectionData = connectionData;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScreenProctoringWindowData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8551477894732539282L;
|
||||
public final String groupId;
|
||||
public final String loginLocation;
|
||||
public final String username;
|
||||
public final String password;
|
||||
|
||||
public ScreenProctoringWindowData(final String groupId, final String loginLocation, final String username,
|
||||
final String password) {
|
||||
super();
|
||||
this.groupId = groupId;
|
||||
this.loginLocation = loginLocation;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -422,6 +422,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
public Result<Boolean> saveSecurityCheckStatus(final Long connectionId, final Boolean checkStatus) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
// TODO fix update time update (SEBSERV-474)
|
||||
|
||||
UpdateDSL.updateWithMapper(
|
||||
this.clientConnectionRecordMapper::update,
|
||||
ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
|
||||
|
@ -440,6 +442,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
public Result<Boolean> saveSEBClientVersionCheckStatus(final Long connectionId, final Boolean checkStatus) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
// TODO fix update time update (SEBSERV-474)
|
||||
|
||||
UpdateDSL.updateWithMapper(
|
||||
this.clientConnectionRecordMapper::update,
|
||||
ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
|
||||
|
|
|
@ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ProctoringSettings
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.RemoteProctoringService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.RemoteProctoringServiceFactory;
|
||||
|
||||
@Lazy
|
||||
|
@ -34,15 +35,18 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
|||
private final ProctoringSettingsDAO proctoringSettingsDAO;
|
||||
private final RemoteProctoringServiceFactory remoteProctoringServiceFactory;
|
||||
private final ScreenProctoringService screenProctoringService;
|
||||
private final ExamSessionCacheService examSessionCacheService;
|
||||
|
||||
public ProctoringAdminServiceImpl(
|
||||
final ProctoringSettingsDAOImpl proctoringSettingsDAO,
|
||||
final RemoteProctoringServiceFactory remoteProctoringServiceFactory,
|
||||
final ScreenProctoringService screenProctoringService) {
|
||||
final ScreenProctoringService screenProctoringService,
|
||||
final ExamSessionCacheService examSessionCacheService) {
|
||||
|
||||
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
||||
this.remoteProctoringServiceFactory = remoteProctoringServiceFactory;
|
||||
this.screenProctoringService = screenProctoringService;
|
||||
this.examSessionCacheService = examSessionCacheService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,10 +69,19 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
|||
|
||||
checkType(parentEntityKey);
|
||||
|
||||
return this.proctoringSettingsDAO
|
||||
final ProctoringServiceSettings result = this.proctoringSettingsDAO
|
||||
.saveProctoringServiceSettings(parentEntityKey, proctoringServiceSettings)
|
||||
.getOrThrow();
|
||||
|
||||
if (parentEntityKey.entityType == EntityType.EXAM) {
|
||||
try {
|
||||
this.examSessionCacheService.evict(Long.parseLong(parentEntityKey.modelId));
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to update Exam cache:_{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -107,6 +120,12 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
|||
.onError(error -> this.proctoringSettingsDAO
|
||||
.disableScreenProctoring(screenProctoringSettings.examId))
|
||||
.getOrThrow();
|
||||
|
||||
try {
|
||||
this.examSessionCacheService.evict(Long.parseLong(parentEntityKey.modelId));
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to update Exam cache:_{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return screenProctoringSettings;
|
||||
|
|
Loading…
Reference in a new issue