SEBSERV-435 implements screen proctoring service linking

This commit is contained in:
anhefti 2023-10-26 16:06:22 +02:00
parent a04c111b59
commit cf1a84a38d
10 changed files with 247 additions and 130 deletions

View file

@ -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;

View file

@ -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;
}

View file

@ -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();
}
}

View file

@ -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 =

View file

@ -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();
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}
}
}

View file

@ -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)

View file

@ -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;