From cf1a84a38d20e2b6605e6b3213b700998cca16fd Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 26 Oct 2023 16:06:22 +0200 Subject: [PATCH] SEBSERV-435 implements screen proctoring service linking --- .../model/exam/ProctoringServiceSettings.java | 63 +++++++++++++++ .../model/exam/ScreenProctoringSettings.java | 27 +++++++ .../seb/sebserver/gui/ProctoringServlet.java | 59 +++++++++++++- .../seb/sebserver/gui/RAPSpringConfig.java | 14 ---- .../gui/ScreenProctoringServlet.java | 80 ------------------- .../monitoring/MonitoringRunningExam.java | 25 +++--- .../MonitoringProctoringService.java | 41 +++++++--- .../proctoring/ProctoringGUIService.java | 41 +++++++++- .../dao/impl/ClientConnectionDAOImpl.java | 4 + .../exam/impl/ProctoringAdminServiceImpl.java | 23 +++++- 10 files changed, 247 insertions(+), 130 deletions(-) delete mode 100644 src/main/java/ch/ethz/seb/sebserver/gui/ScreenProctoringServlet.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringServiceSettings.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringServiceSettings.java index 2604a889..eb50a852 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringServiceSettings.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ProctoringServiceSettings.java @@ -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 getEnabledFeatures(final Map 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; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ScreenProctoringSettings.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ScreenProctoringSettings.java index 5bea929f..c99a1209 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ScreenProctoringSettings.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/ScreenProctoringSettings.java @@ -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; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java index 73321a3e..2fe48afe 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java @@ -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(""); + sb.append(""); + sb.append("
"); + sb.append(""); + sb.append(""); + sb.append("
"); + sb.append(""); + sb.append(""); + + 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(); } } \ No newline at end of file diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/RAPSpringConfig.java b/src/main/java/ch/ethz/seb/sebserver/gui/RAPSpringConfig.java index 978c53ea..b4c2df1d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/RAPSpringConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/RAPSpringConfig.java @@ -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 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 = diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/ScreenProctoringServlet.java b/src/main/java/ch/ethz/seb/sebserver/gui/ScreenProctoringServlet.java deleted file mode 100644 index 1f97d74c..00000000 --- a/src/main/java/ch/ethz/seb/sebserver/gui/ScreenProctoringServlet.java +++ /dev/null @@ -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(""); -// sb.AppendFormat(@""); -// sb.AppendFormat("
",postbackUrl); -// sb.AppendFormat("", id); -// // Other params go here -// sb.Append("
"); -// sb.Append(""); -// sb.Append(""); - - 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(); - } - -} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index 84ed77dc..27ca018f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -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 collectingRooms = (proctoringEnabled) ? this.pageService diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java index 08aa0047..131ffda5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/MonitoringProctoringService.java @@ -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 redirect = new RestTemplate().exchange( + // TODO make this configurable or static + final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location"; + final ResponseEntity 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; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java index 9ee7d793..d8070bf7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/session/proctoring/ProctoringGUIService.java @@ -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; + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java index 8969e753..f7eee0f9 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java @@ -422,6 +422,8 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { public Result 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 saveSEBClientVersionCheckStatus(final Long connectionId, final Boolean checkStatus) { return Result.tryCatch(() -> { + // TODO fix update time update (SEBSERV-474) + UpdateDSL.updateWithMapper( this.clientConnectionRecordMapper::update, ClientConnectionRecordDynamicSqlSupport.clientConnectionRecord) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java index 6ae0dde6..240cd90a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ProctoringAdminServiceImpl.java @@ -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;