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;
|
package ch.ethz.seb.sebserver.gbl.model.exam;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
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.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
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.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
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.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
|
@ -177,6 +182,64 @@ public class ProctoringServiceSettings implements Entity {
|
||||||
this.useZoomAppClientForCollectingRoom = copyOf.useZoomAppClientForCollectingRoom;
|
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
|
@Override
|
||||||
public String getModelId() {
|
public String getModelId() {
|
||||||
return (this.examId != null) ? String.valueOf(this.examId) : null;
|
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 java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.hibernate.validator.constraints.URL;
|
import org.hibernate.validator.constraints.URL;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@ -83,6 +85,31 @@ public class ScreenProctoringSettings {
|
||||||
this.collectingGroupSize = collectingGroupSize;
|
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() {
|
public Long getExamId() {
|
||||||
return this.examId;
|
return this.examId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
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.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
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.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;
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
|
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.ProctoringWindowData;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ScreenProctoringWindowData;
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringWindowScriptResolver;
|
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringWindowScriptResolver;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
public class ProctoringServlet extends HttpServlet {
|
public class ProctoringServlet extends HttpServlet {
|
||||||
|
|
||||||
|
public static final String SCREEN_PROCOTRING_FLAG_PARAM = "screenproctoring";
|
||||||
|
|
||||||
private static final long serialVersionUID = 3475978419653411800L;
|
private static final long serialVersionUID = 3475978419653411800L;
|
||||||
private static final Logger log = LoggerFactory.getLogger(ProctoringServlet.class);
|
private static final Logger log = LoggerFactory.getLogger(ProctoringServlet.class);
|
||||||
|
|
||||||
|
@ -58,12 +63,54 @@ public class ProctoringServlet extends HttpServlet {
|
||||||
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
||||||
.getRequiredWebApplicationContext(servletContext);
|
.getRequiredWebApplicationContext(servletContext);
|
||||||
|
|
||||||
final boolean authenticated = isAuthenticated(httpSession, webApplicationContext);
|
UserInfo user;
|
||||||
if (!authenticated) {
|
try {
|
||||||
|
user = isAuthenticated(httpSession, webApplicationContext);
|
||||||
|
} catch (final Exception e) {
|
||||||
resp.setStatus(HttpStatus.FORBIDDEN.value());
|
resp.setStatus(HttpStatus.FORBIDDEN.value());
|
||||||
return;
|
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 =
|
final ProctoringWindowData proctoringData =
|
||||||
(ProctoringWindowData) httpSession
|
(ProctoringWindowData) httpSession
|
||||||
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
|
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
|
||||||
|
@ -89,7 +136,7 @@ public class ProctoringServlet extends HttpServlet {
|
||||||
resp.setStatus(HttpServletResponse.SC_OK);
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAuthenticated(
|
private UserInfo isAuthenticated(
|
||||||
final HttpSession httpSession,
|
final HttpSession httpSession,
|
||||||
final WebApplicationContext webApplicationContext) {
|
final WebApplicationContext webApplicationContext) {
|
||||||
|
|
||||||
|
@ -97,7 +144,11 @@ public class ProctoringServlet extends HttpServlet {
|
||||||
.getBean(AuthorizationContextHolder.class);
|
.getBean(AuthorizationContextHolder.class);
|
||||||
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
||||||
.getAuthorizationContext(httpSession);
|
.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}")
|
@Value("${sebserver.gui.remote.proctoring.api-servler.endpoint:/remote-view-servlet}")
|
||||||
private String remoteProctoringViewServletEndpoint;
|
private String remoteProctoringViewServletEndpoint;
|
||||||
|
|
||||||
@Value("${sebserver.gui.screen.proctoring.api-servler.endpoint:/screen-proctoring}")
|
|
||||||
private String screenProctoringViewServletEndpoint;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public StaticApplicationPropertyResolver staticApplicationPropertyResolver() {
|
public StaticApplicationPropertyResolver staticApplicationPropertyResolver() {
|
||||||
return new StaticApplicationPropertyResolver();
|
return new StaticApplicationPropertyResolver();
|
||||||
|
@ -86,17 +83,6 @@ public class RAPSpringConfig {
|
||||||
this.remoteProctoringEndpoint + this.remoteProctoringViewServletEndpoint + "/*");
|
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
|
@Bean
|
||||||
public MessageSource messageSource() {
|
public MessageSource messageSource() {
|
||||||
final ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource =
|
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.push.ServerPushService;
|
||||||
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.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.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.exam.indicator.GetIndicators;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRooms;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetCollectingRooms;
|
||||||
|
@ -279,18 +277,8 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
clientTable,
|
clientTable,
|
||||||
isExamSupporter));
|
isExamSupporter));
|
||||||
|
|
||||||
final ProctoringServiceSettings proctoringSettings = this.restService
|
final ProctoringServiceSettings proctoringSettings = new ProctoringServiceSettings(exam);
|
||||||
.getBuilder(GetExamProctoringSettings.class)
|
final ScreenProctoringSettings screenProctoringSettings = new ScreenProctoringSettings(exam);
|
||||||
.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);
|
|
||||||
|
|
||||||
guiUpdates.add(createProctoringActions(
|
guiUpdates.add(createProctoringActions(
|
||||||
proctoringSettings,
|
proctoringSettings,
|
||||||
screenProctoringSettings,
|
screenProctoringSettings,
|
||||||
|
@ -330,7 +318,12 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
final boolean proctoringEnabled = proctoringSettings != null &&
|
final boolean proctoringEnabled = proctoringSettings != null &&
|
||||||
BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
|
BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
|
||||||
final boolean screenProctoringEnabled = screenProctoringSettings != null &&
|
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)) {
|
if (proctoringEnabled && proctoringSettings.enabledFeatures.contains(ProctoringFeature.TOWN_HALL)) {
|
||||||
final EntityKey entityKey = pageContext.getEntityKey();
|
final EntityKey entityKey = pageContext.getEntityKey();
|
||||||
|
@ -361,7 +354,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proctoringGUIService.clearCollectingRoomActionState();
|
proctoringGUIService.clearActionState();
|
||||||
final EntityKey entityKey = pageContext.getEntityKey();
|
final EntityKey entityKey = pageContext.getEntityKey();
|
||||||
final Collection<RemoteProctoringRoom> collectingRooms = (proctoringEnabled)
|
final Collection<RemoteProctoringRoom> collectingRooms = (proctoringEnabled)
|
||||||
? this.pageService
|
? this.pageService
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
package ch.ethz.seb.sebserver.gui.service.session.proctoring;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
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.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.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.content.action.ActionPane;
|
import ch.ethz.seb.sebserver.gui.content.action.ActionPane;
|
||||||
import ch.ethz.seb.sebserver.gui.content.monitoring.ProctorRoomConnectionsPopup;
|
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.GetProctorRoomConnection;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.IsTownhallRoomAvailable;
|
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.api.session.NotifyProctoringRoomOpened;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
|
@ -97,9 +98,7 @@ public class MonitoringProctoringService {
|
||||||
private final JSONMapper jsonMapper;
|
private final JSONMapper jsonMapper;
|
||||||
private final Resource openRoomScriptRes;
|
private final Resource openRoomScriptRes;
|
||||||
private final String remoteProctoringEndpoint;
|
private final String remoteProctoringEndpoint;
|
||||||
|
private final String remoteProctoringViewServletEndpoint;
|
||||||
@Value("${sebserver.gui.screen.proctoring.api-servler.endpoint:/screen-proctoring}")
|
|
||||||
private String screenProctoringViewServletEndpoint;
|
|
||||||
|
|
||||||
public MonitoringProctoringService(
|
public MonitoringProctoringService(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
|
@ -107,7 +106,8 @@ public class MonitoringProctoringService {
|
||||||
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
||||||
final JSONMapper jsonMapper,
|
final JSONMapper jsonMapper,
|
||||||
@Value(OPEN_ROOM_SCRIPT_RES) final Resource openRoomScript,
|
@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.pageService = pageService;
|
||||||
this.guiServiceInfo = guiServiceInfo;
|
this.guiServiceInfo = guiServiceInfo;
|
||||||
|
@ -115,6 +115,7 @@ public class MonitoringProctoringService {
|
||||||
this.jsonMapper = jsonMapper;
|
this.jsonMapper = jsonMapper;
|
||||||
this.openRoomScriptRes = openRoomScript;
|
this.openRoomScriptRes = openRoomScript;
|
||||||
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
||||||
|
this.remoteProctoringViewServletEndpoint = remoteProctoringViewServletEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTownhallRoomActive(final String examModelId) {
|
public boolean isTownhallRoomActive(final String examModelId) {
|
||||||
|
@ -313,19 +314,33 @@ public class MonitoringProctoringService {
|
||||||
final ScreenProctoringGroup group,
|
final ScreenProctoringGroup group,
|
||||||
final PageAction _action) {
|
final PageAction _action) {
|
||||||
|
|
||||||
final String serviceRedirect = settings.spsServiceURL + "/guilogin";
|
// TODO make this configurable or static
|
||||||
final ResponseEntity<Void> redirect = new RestTemplate().exchange(
|
final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location";
|
||||||
|
final ResponseEntity<String> redirect = new RestTemplate().exchange(
|
||||||
serviceRedirect,
|
serviceRedirect,
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
null,
|
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 UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
|
||||||
final String url = this.remoteProctoringEndpoint
|
final String url = this.guiServiceInfo.getExternalServerURIBuilder().toUriString()
|
||||||
+ this.screenProctoringViewServletEndpoint
|
+ this.remoteProctoringEndpoint
|
||||||
+ "?group=" + group.uuid
|
+ this.remoteProctoringViewServletEndpoint
|
||||||
+ "&loc=" + redirectLocation;
|
+ Constants.SLASH
|
||||||
|
+ Constants.QUERY
|
||||||
|
+ ProctoringServlet.SCREEN_PROCOTRING_FLAG_PARAM
|
||||||
|
+ Constants.EQUALITY_SIGN
|
||||||
|
+ Constants.TRUE_STRING;
|
||||||
|
|
||||||
launcher.openURL(url);
|
launcher.openURL(url);
|
||||||
return _action;
|
return _action;
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,8 +129,9 @@ public class ProctoringGUIService {
|
||||||
.reduce(0, (acc, room) -> acc + room.a.roomSize, Integer::sum);
|
.reduce(0, (acc, room) -> acc + room.a.roomSize, Integer::sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCollectingRoomActionState() {
|
public void clearActionState() {
|
||||||
this.collectingRoomsActionState.clear();
|
this.collectingRoomsActionState.clear();
|
||||||
|
this.screenProctoringGroupState.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean registerProctoringWindow(
|
public boolean registerProctoringWindow(
|
||||||
|
@ -157,12 +158,28 @@ public class ProctoringGUIService {
|
||||||
.getAttribute(SESSION_ATTR_PROCTORING_DATA);
|
.getAttribute(SESSION_ATTR_PROCTORING_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ScreenProctoringWindowData getCurrentScreemProctoringWindowData() {
|
||||||
|
return (ScreenProctoringWindowData) RWT.getUISession()
|
||||||
|
.getHttpSession()
|
||||||
|
.getAttribute(SESSION_ATTR_SCREEN_PROCTORING_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
public static void setCurrentProctoringWindowData(
|
public static void setCurrentProctoringWindowData(
|
||||||
final String examId,
|
final String examId,
|
||||||
final ProctoringRoomConnection data) {
|
final ProctoringRoomConnection data) {
|
||||||
setCurrentProctoringWindowData(examId, data.roomName, 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(
|
public static void setCurrentProctoringWindowData(
|
||||||
final String examId,
|
final String examId,
|
||||||
final String windowName,
|
final String windowName,
|
||||||
|
@ -226,6 +243,7 @@ public class ProctoringGUIService {
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
this.collectingRoomsActionState.clear();
|
this.collectingRoomsActionState.clear();
|
||||||
|
this.screenProctoringGroupState.clear();
|
||||||
if (!this.openWindows.isEmpty()) {
|
if (!this.openWindows.isEmpty()) {
|
||||||
new HashSet<>(this.openWindows.entrySet())
|
new HashSet<>(this.openWindows.entrySet())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -265,6 +283,8 @@ public class ProctoringGUIService {
|
||||||
|
|
||||||
public static class ProctoringWindowData implements Serializable {
|
public static class ProctoringWindowData implements Serializable {
|
||||||
private static final long serialVersionUID = -9060185011534956417L;
|
private static final long serialVersionUID = -9060185011534956417L;
|
||||||
|
|
||||||
|
public final boolean isScreenProctoring;
|
||||||
public final String windowName;
|
public final String windowName;
|
||||||
public final String examId;
|
public final String examId;
|
||||||
public final ProctoringRoomConnection connectionData;
|
public final ProctoringRoomConnection connectionData;
|
||||||
|
@ -273,10 +293,29 @@ public class ProctoringGUIService {
|
||||||
final String examId,
|
final String examId,
|
||||||
final String windowName,
|
final String windowName,
|
||||||
final ProctoringRoomConnection connectionData) {
|
final ProctoringRoomConnection connectionData) {
|
||||||
|
this.isScreenProctoring = false;
|
||||||
this.windowName = windowName;
|
this.windowName = windowName;
|
||||||
this.examId = examId;
|
this.examId = examId;
|
||||||
this.connectionData = connectionData;
|
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) {
|
public Result<Boolean> saveSecurityCheckStatus(final Long connectionId, final Boolean checkStatus) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
// TODO fix update time update (SEBSERV-474)
|
||||||
|
|
||||||
UpdateDSL.updateWithMapper(
|
UpdateDSL.updateWithMapper(
|
||||||
this.clientConnectionRecordMapper::update,
|
this.clientConnectionRecordMapper::update,
|
||||||
ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
|
ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
|
||||||
|
@ -440,6 +442,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
||||||
public Result<Boolean> saveSEBClientVersionCheckStatus(final Long connectionId, final Boolean checkStatus) {
|
public Result<Boolean> saveSEBClientVersionCheckStatus(final Long connectionId, final Boolean checkStatus) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
// TODO fix update time update (SEBSERV-474)
|
||||||
|
|
||||||
UpdateDSL.updateWithMapper(
|
UpdateDSL.updateWithMapper(
|
||||||
this.clientConnectionRecordMapper::update,
|
this.clientConnectionRecordMapper::update,
|
||||||
ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord)
|
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.exam.ProctoringAdminService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.RemoteProctoringService;
|
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.ScreenProctoringService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.RemoteProctoringServiceFactory;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.RemoteProctoringServiceFactory;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -34,15 +35,18 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
||||||
private final ProctoringSettingsDAO proctoringSettingsDAO;
|
private final ProctoringSettingsDAO proctoringSettingsDAO;
|
||||||
private final RemoteProctoringServiceFactory remoteProctoringServiceFactory;
|
private final RemoteProctoringServiceFactory remoteProctoringServiceFactory;
|
||||||
private final ScreenProctoringService screenProctoringService;
|
private final ScreenProctoringService screenProctoringService;
|
||||||
|
private final ExamSessionCacheService examSessionCacheService;
|
||||||
|
|
||||||
public ProctoringAdminServiceImpl(
|
public ProctoringAdminServiceImpl(
|
||||||
final ProctoringSettingsDAOImpl proctoringSettingsDAO,
|
final ProctoringSettingsDAOImpl proctoringSettingsDAO,
|
||||||
final RemoteProctoringServiceFactory remoteProctoringServiceFactory,
|
final RemoteProctoringServiceFactory remoteProctoringServiceFactory,
|
||||||
final ScreenProctoringService screenProctoringService) {
|
final ScreenProctoringService screenProctoringService,
|
||||||
|
final ExamSessionCacheService examSessionCacheService) {
|
||||||
|
|
||||||
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
||||||
this.remoteProctoringServiceFactory = remoteProctoringServiceFactory;
|
this.remoteProctoringServiceFactory = remoteProctoringServiceFactory;
|
||||||
this.screenProctoringService = screenProctoringService;
|
this.screenProctoringService = screenProctoringService;
|
||||||
|
this.examSessionCacheService = examSessionCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,10 +69,19 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
||||||
|
|
||||||
checkType(parentEntityKey);
|
checkType(parentEntityKey);
|
||||||
|
|
||||||
return this.proctoringSettingsDAO
|
final ProctoringServiceSettings result = this.proctoringSettingsDAO
|
||||||
.saveProctoringServiceSettings(parentEntityKey, proctoringServiceSettings)
|
.saveProctoringServiceSettings(parentEntityKey, proctoringServiceSettings)
|
||||||
.getOrThrow();
|
.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
|
.onError(error -> this.proctoringSettingsDAO
|
||||||
.disableScreenProctoring(screenProctoringSettings.examId))
|
.disableScreenProctoring(screenProctoringSettings.examId))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.examSessionCacheService.evict(Long.parseLong(parentEntityKey.modelId));
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.warn("Failed to update Exam cache:_{}", e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return screenProctoringSettings;
|
return screenProctoringSettings;
|
||||||
|
|
Loading…
Reference in a new issue