diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java
index 202ca6dc..2f0ed7dd 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java
@@ -56,6 +56,7 @@ public final class API {
public static final String OAUTH_ENDPOINT = "/oauth";
public static final String OAUTH_TOKEN_ENDPOINT = OAUTH_ENDPOINT + "/token";
+ public static final String OAUTH_JWTTOKEN_ENDPOINT = OAUTH_ENDPOINT + "/jwttoken";
public static final String OAUTH_REVOKE_TOKEN_ENDPOINT = OAUTH_ENDPOINT + "/revoke-token";
public static final String CURRENT_USER_PATH_SEGMENT = "/me";
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 c99a1209..9d5a3ef5 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
@@ -34,6 +34,8 @@ public class ScreenProctoringSettings {
public static final String ATTR_SPS_ACCOUNT_ID = "spsAccountId";
public static final String ATTR_SPS_ACCOUNT_PASSWORD = "spsAccountPassword";
+ public static final String ATTR_SPS_BUNDLED = "bundled";
+
@JsonProperty(Domain.EXAM.ATTR_ID)
public final Long examId;
@@ -62,6 +64,9 @@ public class ScreenProctoringSettings {
@JsonProperty(ATTR_COLLECTING_GROUP_SIZE)
public final Integer collectingGroupSize;
+ @JsonProperty(ATTR_SPS_BUNDLED)
+ public final boolean bundled;
+
@JsonCreator
public ScreenProctoringSettings(
@JsonProperty(Domain.EXAM.ATTR_ID) final Long examId,
@@ -72,7 +77,8 @@ public class ScreenProctoringSettings {
@JsonProperty(ATTR_SPS_ACCOUNT_ID) final String spsAccountId,
@JsonProperty(ATTR_SPS_ACCOUNT_PASSWORD) final CharSequence spsAccountPassword,
@JsonProperty(ATTR_COLLECTING_STRATEGY) final CollectingStrategy collectingStrategy,
- @JsonProperty(ATTR_COLLECTING_GROUP_SIZE) final Integer collectingGroupSize) {
+ @JsonProperty(ATTR_COLLECTING_GROUP_SIZE) final Integer collectingGroupSize,
+ @JsonProperty(ATTR_SPS_BUNDLED) final boolean bundled) {
this.examId = examId;
this.enableScreenProctoring = enableScreenProctoring;
@@ -83,6 +89,30 @@ public class ScreenProctoringSettings {
this.spsAccountPassword = spsAccountPassword;
this.collectingStrategy = collectingStrategy;
this.collectingGroupSize = collectingGroupSize;
+ this.bundled = bundled;
+ }
+
+ public ScreenProctoringSettings(
+ final Long examId,
+ final Boolean enableScreenProctoring,
+ final String spsServiceURL,
+ final String spsAPIKey,
+ final CharSequence spsAPISecret,
+ final String spsAccountId,
+ final CharSequence spsAccountPassword,
+ final CollectingStrategy collectingStrategy,
+ final Integer collectingGroupSize) {
+
+ this.examId = examId;
+ this.enableScreenProctoring = enableScreenProctoring;
+ this.spsServiceURL = spsServiceURL;
+ this.spsAPIKey = spsAPIKey;
+ this.spsAPISecret = spsAPISecret;
+ this.spsAccountId = spsAccountId;
+ this.spsAccountPassword = spsAccountPassword;
+ this.collectingStrategy = collectingStrategy;
+ this.collectingGroupSize = collectingGroupSize;
+ this.bundled = false;
}
public ScreenProctoringSettings(final Exam exam) {
@@ -108,6 +138,7 @@ public class ScreenProctoringSettings {
this.collectingGroupSize = Integer.parseInt(exam.additionalAttributes.getOrDefault(
ATTR_COLLECTING_GROUP_SIZE,
"-1"));
+ this.bundled = false;
}
public Long getExamId() {
@@ -151,6 +182,10 @@ public class ScreenProctoringSettings {
return Objects.hash(this.examId);
}
+ public boolean isBundled() {
+ return this.bundled;
+ }
+
@Override
public boolean equals(final Object obj) {
if (this == obj)
diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java
index 7e0d736b..af87fc40 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java
@@ -39,6 +39,7 @@ import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
+import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@@ -919,4 +920,12 @@ public final class Utils {
.replaceAll("[^A-Za-z0-9_]", "");
}
+ public static String createBasicAuthHeader(final String clientname, final CharSequence clientsecret) {
+ final String plainCreds = clientname + Constants.COLON + clientsecret;
+ final byte[] plainCredsBytes = plainCreds.getBytes();
+ final byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
+ final String base64Creds = new String(base64CredsBytes);
+ return "Basic " + base64Creds;
+ }
+
}
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 209eca4c..73321a3e 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
@@ -18,7 +18,6 @@ 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;
@@ -26,21 +25,17 @@ 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);
@@ -63,54 +58,12 @@ public class ProctoringServlet extends HttpServlet {
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
.getRequiredWebApplicationContext(servletContext);
- UserInfo user;
- try {
- user = isAuthenticated(httpSession, webApplicationContext);
- } catch (final Exception e) {
+ final boolean authenticated = isAuthenticated(httpSession, webApplicationContext);
+ if (!authenticated) {
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("");
-
- 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);
@@ -136,7 +89,7 @@ public class ProctoringServlet extends HttpServlet {
resp.setStatus(HttpServletResponse.SC_OK);
}
- private UserInfo isAuthenticated(
+ private boolean isAuthenticated(
final HttpSession httpSession,
final WebApplicationContext webApplicationContext) {
@@ -144,11 +97,7 @@ public class ProctoringServlet extends HttpServlet {
.getBean(AuthorizationContextHolder.class);
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
.getAuthorizationContext(httpSession);
- if (!authorizationContext.isValid() || !authorizationContext.isLoggedIn()) {
- throw new RuntimeException("No authentication found");
- }
-
- return authorizationContext.getLoggedInUser().getOrThrow();
+ return authorizationContext.isValid() && authorizationContext.isLoggedIn();
}
}
\ No newline at end of file
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ScreenProctoringSettingsPopup.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ScreenProctoringSettingsPopup.java
index 5cf42840..dddbc0db 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ScreenProctoringSettingsPopup.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ScreenProctoringSettingsPopup.java
@@ -199,7 +199,13 @@ public class ScreenProctoringSettingsPopup {
new ActionEvent(action),
action.pageContext());
return true;
+ } else {
+ final String bundled = formHandle.getForm().getStaticValue(ScreenProctoringSettings.ATTR_SPS_BUNDLED);
+ if (bundled != null) {
+ pageContext.notifyActivationError(EntityType.SCREEN_PROCTORING_GROUP, saveRequest.getError());
+ }
}
+
return false;
}
@@ -243,11 +249,14 @@ public class ScreenProctoringSettingsPopup {
this.pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
final FormHandle form = this.pageService.formBuilder(formContext)
+ .putStaticValueIf(
+ () -> settings.bundled,
+ ScreenProctoringSettings.ATTR_SPS_BUNDLED,
+ Constants.TRUE_STRING)
.withDefaultSpanInput(5)
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(1)
.readonly(isReadonly)
-
.addField(FormBuilder.text(
"Info",
FORM_INFO_TITLE,
@@ -265,33 +274,39 @@ public class ScreenProctoringSettingsPopup {
ScreenProctoringSettings.ATTR_SPS_SERVICE_URL,
FORM_URL,
settings.spsServiceURL)
- .mandatory())
+ .mandatory()
+ .readonly(settings.bundled))
.addField(FormBuilder.text(
ScreenProctoringSettings.ATTR_SPS_API_KEY,
FORM_APPKEY_SPS,
- settings.spsAPIKey))
+ settings.spsAPIKey)
+ .readonly(settings.bundled))
.withEmptyCellSeparation(false)
- .addField(FormBuilder.password(
- ScreenProctoringSettings.ATTR_SPS_API_SECRET,
- FORM_APPSECRET_SPS,
- (settings.spsAPISecret != null)
- ? String.valueOf(settings.spsAPISecret)
- : null))
+ .addFieldIf(
+ () -> !settings.bundled,
+ () -> FormBuilder.password(
+ ScreenProctoringSettings.ATTR_SPS_API_SECRET,
+ FORM_APPSECRET_SPS,
+ (settings.spsAPISecret != null)
+ ? String.valueOf(settings.spsAPISecret)
+ : null))
.addField(FormBuilder.text(
ScreenProctoringSettings.ATTR_SPS_ACCOUNT_ID,
FORM_ACCOUNT_ID_SPS,
- settings.spsAccountId))
+ settings.spsAccountId)
+ .readonly(settings.bundled))
.withEmptyCellSeparation(false)
-
- .addField(FormBuilder.password(
- ScreenProctoringSettings.ATTR_SPS_ACCOUNT_PASSWORD,
- FORM_ACCOUNT_SECRET_SPS,
- (settings.spsAccountPassword != null)
- ? String.valueOf(settings.spsAccountPassword)
- : null))
+ .addFieldIf(
+ () -> !settings.bundled,
+ () -> FormBuilder.password(
+ ScreenProctoringSettings.ATTR_SPS_ACCOUNT_PASSWORD,
+ FORM_ACCOUNT_SECRET_SPS,
+ (settings.spsAccountPassword != null)
+ ? String.valueOf(settings.spsAccountPassword)
+ : null))
.build();
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java
index 3e4a6a57..af133611 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/form/Form.java
@@ -108,6 +108,10 @@ public final class Form implements FormBinding {
}
}
+ public String getStaticValue(final String name) {
+ return this.staticValues.get(name);
+ }
+
public void addToGroup(final String groupName, final String fieldName) {
if (this.formFields.containsKey(fieldName)) {
this.groups.computeIfAbsent(groupName, k -> new HashSet<>())
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java
index 29b160de..6a720e11 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/OAuth2AuthorizationContextHolder.java
@@ -212,6 +212,14 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
return true;
}
+ @Override
+ public CharSequence getUserPassword() {
+ if (isLoggedIn()) {
+ return this.resource.getPassword();
+ }
+ return null;
+ }
+
@Override
public boolean login(final String username, final CharSequence password) {
if (!this.valid || this.isLoggedIn()) {
@@ -363,6 +371,5 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
}
}
}
-
}
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java
index 7db177c2..292f25b0 100644
--- a/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java
+++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/remote/webservice/auth/SEBServerAuthorizationContext.java
@@ -63,4 +63,6 @@ public interface SEBServerAuthorizationContext {
* @return the underling RestTemplate to connect and communicate with the SEB Server webservice */
RestTemplate getRestTemplate();
+ CharSequence getUserPassword();
+
}
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 131ffda5..491e6b1f 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
@@ -27,7 +27,10 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@@ -47,10 +50,10 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
import ch.ethz.seb.sebserver.gbl.model.session.ScreenProctoringGroup;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
+import ch.ethz.seb.sebserver.gbl.util.Cryptor;
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;
@@ -98,16 +101,16 @@ public class MonitoringProctoringService {
private final JSONMapper jsonMapper;
private final Resource openRoomScriptRes;
private final String remoteProctoringEndpoint;
- private final String remoteProctoringViewServletEndpoint;
+ private final Cryptor cryptor;
public MonitoringProctoringService(
final PageService pageService,
final GuiServiceInfo guiServiceInfo,
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
final JSONMapper jsonMapper,
+ final Cryptor cryptor,
@Value(OPEN_ROOM_SCRIPT_RES) final Resource openRoomScript,
- @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) {
+ @Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint) {
this.pageService = pageService;
this.guiServiceInfo = guiServiceInfo;
@@ -115,7 +118,7 @@ public class MonitoringProctoringService {
this.jsonMapper = jsonMapper;
this.openRoomScriptRes = openRoomScript;
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
- this.remoteProctoringViewServletEndpoint = remoteProctoringViewServletEndpoint;
+ this.cryptor = cryptor;
}
public boolean isTownhallRoomActive(final String examModelId) {
@@ -314,34 +317,55 @@ public class MonitoringProctoringService {
final ScreenProctoringGroup group,
final PageAction _action) {
- // TODO make this configurable or static
- final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location";
- final ResponseEntity redirect = new RestTemplate().exchange(
- serviceRedirect,
- HttpMethod.GET,
- null,
- String.class);
+ try {
+ // Get login Token for user login from SPS service
+ final RestTemplate restTemplate = new RestTemplate();
+ final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location";
+ final ResponseEntity redirect = restTemplate.exchange(
+ serviceRedirect,
+ HttpMethod.GET,
+ null,
+ String.class);
- final String redirectLocation = redirect.getBody();
- final CurrentUser currentUser = this.pageService.getCurrentUser();
+ // JWT token request URL
+ final String jwtTokenURL = settings.spsServiceURL + API.OAUTH_JWTTOKEN_ENDPOINT;
- ProctoringGUIService.setCurrentScreenProctoringWindowData(
- group.uuid,
- redirectLocation,
- currentUser.get().username,
- "admin");
+ // Basic Auth header and content type header
+ final HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.add(
+ HttpHeaders.AUTHORIZATION,
+ Utils.createBasicAuthHeader(
+ settings.spsAPIKey,
+ this.cryptor.decrypt(settings.getSpsAPISecret()).getOrThrow()));
+ httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
- final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
- 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;
+ // user credential and redirect info for jwt token request in body - form URL encoded format
+ final CurrentUser currentUser = this.pageService.getCurrentUser();
+ final CharSequence userPassword = currentUser
+ .getAuthorizationContextHolder()
+ .getAuthorizationContext()
+ .getUserPassword();
+ final String body = "username=" + currentUser.get().username
+ + "&password=" + userPassword.toString()
+ + "&redirect=/galleryView/" + group.uuid;
- launcher.openURL(url);
+ // apply jwt token request
+ final HttpEntity httpEntity = new HttpEntity<>(body, httpHeaders);
+ final ResponseEntity tokenRequest = restTemplate.exchange(
+ jwtTokenURL,
+ HttpMethod.POST,
+ httpEntity,
+ String.class);
+
+ // Open SPS Gui redirect URL with login token (jwt token) in new browser tab
+ final String redirectLocation = redirect.getBody() + "/jwt?token=" + tokenRequest.getBody();
+ final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
+ launcher.openURL(redirectLocation);
+ } catch (final Exception e) {
+ log.error("Failed to open screen proctoring service group gallery view: ", e);
+ _action.pageContext()
+ .notifyError(new LocTextKey("Failed to open screen proctoring service group gallery view"), e);
+ }
return _action;
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java
index 340cca32..26976b85 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java
@@ -30,6 +30,7 @@ import org.springframework.web.util.UriComponentsBuilder;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
+import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.WebserviceInfoDAO;
@Lazy
@@ -80,9 +81,12 @@ public class WebserviceInfo {
@Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}")
private int examAPITokenValiditySeconds;
+ private final ScreenProctoringServiceBundle screenProctoringServiceBundle;
+
public WebserviceInfo(
final WebserviceInfoDAO webserviceInfoDAO,
- final Environment environment) {
+ final Environment environment,
+ final Cryptor cryptor) {
this.webserviceInfoDAO = webserviceInfoDAO;
this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY);
@@ -145,6 +149,24 @@ public class WebserviceInfo {
} else {
this.lmsExternalAddressAlias = Collections.emptyMap();
}
+
+ final boolean spsBundled = BooleanUtils.toBoolean(environment.getProperty(
+ "sebserver.feature.seb.screenProctoring.bundled",
+ Constants.FALSE_STRING));
+ if (spsBundled) {
+ this.screenProctoringServiceBundle = new ScreenProctoringServiceBundle(
+ environment.getProperty("sebserver.feature.seb.screenProctoring.bundled.url"),
+ environment.getProperty("sebserver.feature.seb.screenProctoring.bundled.clientId"),
+ cryptor.encrypt(
+ environment.getProperty("sebserver.feature.seb.screenProctoring.bundled.clientPassword"))
+ .getOrThrow(),
+ environment.getProperty("sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username"),
+ cryptor.encrypt(environment
+ .getProperty("sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.password"))
+ .getOrThrow());
+ } else {
+ this.screenProctoringServiceBundle = new ScreenProctoringServiceBundle();
+ }
}
public boolean isMaster() {
@@ -207,6 +229,10 @@ public class WebserviceInfo {
return this.distributedUpdateInterval;
}
+ public ScreenProctoringServiceBundle getScreenProctoringServiceBundle() {
+ return this.screenProctoringServiceBundle;
+ }
+
public String getLocalHostName() {
try {
return InetAddress.getLocalHost().getHostName();
@@ -300,4 +326,53 @@ public class WebserviceInfo {
return builder.toString();
}
+ public static final class ScreenProctoringServiceBundle {
+
+ public final boolean bundled;
+ public final String serviceURL;
+ public final String clientId;
+ public final CharSequence clientSecret;
+ public final String apiAccountName;
+ public final CharSequence apiAccountPassword;
+
+ public ScreenProctoringServiceBundle(
+ final String serviceURL,
+ final String clientId,
+ final CharSequence clientSecret,
+ final String apiAccountName,
+ final CharSequence apiAccountPassword) {
+
+ this.bundled = true;
+ this.serviceURL = serviceURL;
+ this.clientId = clientId;
+ this.clientSecret = clientSecret;
+ this.apiAccountName = apiAccountName;
+ this.apiAccountPassword = apiAccountPassword;
+ }
+
+ public ScreenProctoringServiceBundle() {
+ this.bundled = false;
+ this.serviceURL = null;
+ this.clientId = null;
+ this.clientSecret = null;
+ this.apiAccountName = null;
+ this.apiAccountPassword = null;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("ScreenProctoringServiceBundle [bundled=");
+ builder.append(this.bundled);
+ builder.append(", serviceURL=");
+ builder.append(this.serviceURL);
+ builder.append(", clientId=");
+ builder.append(this.clientId);
+ builder.append(", apiAccountName=");
+ builder.append(this.apiAccountName);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java
index b6f9c046..31723aa5 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java
@@ -22,8 +22,8 @@ import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.SEBServerInit;
import ch.ethz.seb.sebserver.SEBServerInitEvent;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
+import ch.ethz.seb.sebserver.webservice.WebserviceInfo.ScreenProctoringServiceBundle;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.WebserviceInfoDAO;
-import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.SEBClientPingServiceFactory;
@Component
@WebServiceProfile
@@ -39,7 +39,6 @@ public class WebserviceInit implements ApplicationListener ");
SEBServerInit.INIT_LOGGER.info("----> Working with ping service: {}",
- this.sebClientPingServiceFactory.getWorkingServiceType());
+ this.environment.getProperty("sebserver.webservice.ping.service.strategy"));
SEBServerInit.INIT_LOGGER.info("----> ");
SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
@@ -153,6 +150,14 @@ public class WebserviceInit implements ApplicationListener admin API refresh token validity: " + this.webserviceInfo.getAdminRefreshTokenValSec() + "s");
SEBServerInit.INIT_LOGGER.info(
"----> exam API access token validity: " + this.webserviceInfo.getExamAPITokenValiditySeconds() + "s");
+
+ final ScreenProctoringServiceBundle spsBundle = this.webserviceInfo.getScreenProctoringServiceBundle();
+ SEBServerInit.INIT_LOGGER.info("----> ");
+ SEBServerInit.INIT_LOGGER.info("----> Screen Proctoring Bundle enabled: {}", spsBundle.bundled);
+ if (spsBundle.bundled) {
+ SEBServerInit.INIT_LOGGER.info("------> {}", spsBundle);
+ }
+
SEBServerInit.INIT_LOGGER.info("----> ");
SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ProctoringSettingsDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ProctoringSettingsDAO.java
index 011ccaa5..d20d7b9d 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ProctoringSettingsDAO.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ProctoringSettingsDAO.java
@@ -21,7 +21,7 @@ public interface ProctoringSettingsDAO {
EntityKey entityKey,
ProctoringServiceSettings proctoringServiceSettings);
- Result getScreenProctoringSettings(EntityKey entityKey);
+ Result getScreenProctoringSettings(EntityKey entityKeyp);
Result storeScreenProctoringSettings(
final EntityKey entityKey,
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 240cd90a..7ea720de 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
@@ -18,7 +18,10 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
+import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.gbl.util.Result;
+import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
+import ch.ethz.seb.sebserver.webservice.WebserviceInfo.ScreenProctoringServiceBundle;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ProctoringSettingsDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ProctoringSettingsDAOImpl;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
@@ -36,17 +39,23 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
private final RemoteProctoringServiceFactory remoteProctoringServiceFactory;
private final ScreenProctoringService screenProctoringService;
private final ExamSessionCacheService examSessionCacheService;
+ private final ScreenProctoringServiceBundle screenProctoringServiceBundle;
+ private final Cryptor cryptor;
public ProctoringAdminServiceImpl(
final ProctoringSettingsDAOImpl proctoringSettingsDAO,
final RemoteProctoringServiceFactory remoteProctoringServiceFactory,
final ScreenProctoringService screenProctoringService,
- final ExamSessionCacheService examSessionCacheService) {
+ final ExamSessionCacheService examSessionCacheService,
+ final WebserviceInfo webserviceInfo,
+ final Cryptor cryptor) {
this.proctoringSettingsDAO = proctoringSettingsDAO;
this.remoteProctoringServiceFactory = remoteProctoringServiceFactory;
this.screenProctoringService = screenProctoringService;
this.examSessionCacheService = examSessionCacheService;
+ this.screenProctoringServiceBundle = webserviceInfo.getScreenProctoringServiceBundle();
+ this.cryptor = cryptor;
}
@Override
@@ -91,9 +100,25 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
checkType(parentEntityKey);
- return this.proctoringSettingsDAO
+ ScreenProctoringSettings settings = this.proctoringSettingsDAO
.getScreenProctoringSettings(parentEntityKey)
.getOrThrow();
+
+ if (this.screenProctoringServiceBundle.bundled) {
+ settings = new ScreenProctoringSettings(
+ settings.examId,
+ settings.enableScreenProctoring,
+ this.screenProctoringServiceBundle.serviceURL,
+ this.screenProctoringServiceBundle.clientId,
+ null,
+ this.screenProctoringServiceBundle.apiAccountName,
+ null,
+ settings.collectingStrategy,
+ settings.collectingGroupSize,
+ true);
+ }
+
+ return settings;
});
}
@@ -106,17 +131,30 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
checkType(parentEntityKey);
+ ScreenProctoringSettings settings = screenProctoringSettings;
+ if (this.screenProctoringServiceBundle.bundled) {
+ settings = new ScreenProctoringSettings(
+ screenProctoringSettings.examId,
+ screenProctoringSettings.enableScreenProctoring,
+ this.screenProctoringServiceBundle.serviceURL,
+ this.screenProctoringServiceBundle.clientId,
+ this.cryptor.decrypt(this.screenProctoringServiceBundle.clientSecret).getOrThrow(),
+ this.screenProctoringServiceBundle.apiAccountName,
+ this.cryptor.decrypt(this.screenProctoringServiceBundle.apiAccountPassword).getOrThrow(),
+ screenProctoringSettings.collectingStrategy,
+ screenProctoringSettings.collectingGroupSize,
+ true);
+ }
+
this.screenProctoringService
- .testSettings(screenProctoringSettings)
- .flatMap(settings -> this.proctoringSettingsDAO.storeScreenProctoringSettings(
- parentEntityKey,
- screenProctoringSettings))
+ .testSettings(settings)
+ .flatMap(s -> this.proctoringSettingsDAO.storeScreenProctoringSettings(parentEntityKey, s))
.getOrThrow();
if (parentEntityKey.entityType == EntityType.EXAM) {
this.screenProctoringService
- .applyScreenProctoingForExam(screenProctoringSettings.examId)
+ .applyScreenProctoingForExam(settings.examId)
.onError(error -> this.proctoringSettingsDAO
.disableScreenProctoring(screenProctoringSettings.examId))
.getOrThrow();
@@ -128,7 +166,7 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
}
}
- return screenProctoringSettings;
+ return settings;
});
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBatchService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBatchService.java
index 804c75e6..bb723318 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBatchService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBatchService.java
@@ -21,6 +21,7 @@ import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
@@ -33,6 +34,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientPingServic
@Lazy
@Component
@WebServiceProfile
+@ConditionalOnExpression("'${sebserver.webservice.ping.service.strategy}'.equals('BATCH')")
public class SEBClientPingBatchService implements SEBClientPingService {
private static final Logger log = LoggerFactory.getLogger(SEBClientPingBatchService.class);
@@ -116,9 +118,9 @@ public class SEBClientPingBatchService implements SEBClientPingService {
+ this.instructions);
this.pings.put(connectionToken, instructionConfirm);
// // TODO is this a good idea or is there another better way to deal with instruction confirm synchronization?
-// if (instruction != null && instruction.contains("\"instruction-confirm\":\"" + instructionConfirm + "\"")) {
-// return null;
-// }
+ if (instruction != null && instruction.contains("\"instruction-confirm\":\"" + instructionConfirm + "\"")) {
+ return null;
+ }
} else if (!this.pings.containsKey(connectionToken)) {
this.pings.put(connectionToken, StringUtils.EMPTY);
}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBlockingService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBlockingService.java
index 3d5c451a..471f8928 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBlockingService.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingBlockingService.java
@@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@@ -22,6 +23,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientPingServic
@Lazy
@Component
@WebServiceProfile
+@ConditionalOnExpression("'${sebserver.webservice.ping.service.strategy}'.equals('BLOCKING')")
public class SEBClientPingBlockingService implements SEBClientPingService {
private static final Logger log = LoggerFactory.getLogger(SEBClientPingBlockingService.class);
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingServiceFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingServiceFactory.java
deleted file mode 100644
index 31cd6ea7..00000000
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientPingServiceFactory.java
+++ /dev/null
@@ -1,73 +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.webservice.servicelayer.session.impl;
-
-import java.util.Collection;
-import java.util.EnumMap;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-
-import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
-import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientPingService;
-
-@Lazy
-@Component
-@WebServiceProfile
-public class SEBClientPingServiceFactory {
-
- private static final Logger log = LoggerFactory.getLogger(SEBClientPingServiceFactory.class);
-
- private final EnumMap serviceMapping =
- new EnumMap<>(SEBClientPingService.PingServiceType.class);
- private final SEBClientPingService.PingServiceType workingServiceType;
-
- public SEBClientPingServiceFactory(
- final Collection serviceBeans,
- @Value("${sebserver.webservice.api.exam.session.ping.service.strategy:BLOCKING}") final String serviceType) {
-
- SEBClientPingService.PingServiceType serviceTypeToSet = SEBClientPingService.PingServiceType.BLOCKING;
- try {
- serviceTypeToSet = SEBClientPingService.PingServiceType.valueOf(serviceType);
- } catch (final Exception e) {
- serviceTypeToSet = SEBClientPingService.PingServiceType.BLOCKING;
- }
- this.workingServiceType = serviceTypeToSet;
-
- serviceBeans.stream().forEach(service -> this.serviceMapping.putIfAbsent(service.pingServiceType(), service));
- }
-
- public SEBClientPingService.PingServiceType getWorkingServiceType() {
- return this.workingServiceType;
- }
-
- public SEBClientPingService getSEBClientPingService() {
-
- log.info("Work with SEBClientPingService of type: {}", this.workingServiceType);
-
- switch (this.workingServiceType) {
- case BATCH: {
- final SEBClientPingService service =
- this.serviceMapping.get(SEBClientPingService.PingServiceType.BATCH);
- if (service != null) {
- ((SEBClientPingBatchService) service).init();
- return service;
- } else {
- return this.serviceMapping.get(SEBClientPingService.PingServiceType.BLOCKING);
- }
- }
- default:
- return this.serviceMapping.get(SEBClientPingService.PingServiceType.BLOCKING);
- }
- }
-
-}
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientSessionServiceImpl.java
index 9d668706..2c34a1be 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientSessionServiceImpl.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SEBClientSessionServiceImpl.java
@@ -60,7 +60,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
final InternalClientConnectionDataFactory internalClientConnectionDataFactory,
final SecurityKeyService securityKeyService,
final SEBClientVersionService sebClientVersionService,
- final SEBClientPingServiceFactory sebClientPingServiceFactory) {
+ final SEBClientPingService sebClientPingService) {
this.clientConnectionDAO = clientConnectionDAO;
this.examSessionService = examSessionService;
@@ -70,7 +70,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
this.securityKeyService = securityKeyService;
this.sebClientVersionService = sebClientVersionService;
- this.sebClientPingService = sebClientPingServiceFactory.getSEBClientPingService();
+ this.sebClientPingService = sebClientPingService;
}
@Override
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java
index b041355b..3c57d397 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java
@@ -223,6 +223,10 @@ class ScreenProctoringAPIBinding {
if (result.getStatusCode() != HttpStatus.OK) {
if (result.getStatusCode().is4xxClientError()) {
+ log.warn(
+ "Failed to establish REST connection to: {}. status: {}",
+ screenProctoringSettings.spsServiceURL, result.getStatusCode());
+
throw new FieldValidationException(
"serverURL",
"screenProctoringSettings:spsServiceURL:url.noAccess");
@@ -302,7 +306,6 @@ class ScreenProctoringAPIBinding {
final SPSData spsData = this.getSPSData(exam.id);
// re-activate all needed entities on SPS side
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, true, apiTemplate);
- activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, true, apiTemplate);
// mark successfully activated on SPS side
this.additionalAttributesDAO.saveAdditionalAttribute(
@@ -367,7 +370,7 @@ class ScreenProctoringAPIBinding {
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.EXAM_ENDPOINT)
.pathSegment(spsData.spsExamUUID)
.build()
@@ -410,8 +413,8 @@ class ScreenProctoringAPIBinding {
}
final SPSData spsData = this.getSPSData(exam.id);
- activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, this.apiTemplate);
- activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, false, this.apiTemplate);
+ final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
+ activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, false, apiTemplate);
// mark successfully dispose on SPS side
this.additionalAttributesDAO.saveAdditionalAttribute(
@@ -490,7 +493,7 @@ class ScreenProctoringAPIBinding {
final String token = clientConnection.getConnectionToken();
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(examId);
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.SESSION_ENDPOINT)
.build()
@@ -567,7 +570,7 @@ class ScreenProctoringAPIBinding {
userInfo.roles);
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.USERSYNC_SEBSERVER_ENDPOINT)
.build()
.toUriString();
@@ -603,7 +606,7 @@ class ScreenProctoringAPIBinding {
.getOrThrow();
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.ENTIY_PRIVILEGES_ENDPOINT)
.build()
.toUriString();
@@ -696,7 +699,7 @@ class ScreenProctoringAPIBinding {
throws JsonMappingException, JsonProcessingException {
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.GROUP_ENDPOINT)
.build()
.toUriString();
@@ -729,7 +732,7 @@ class ScreenProctoringAPIBinding {
try {
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.EXAM_ENDPOINT)
.build().toUriString();
@@ -773,7 +776,7 @@ class ScreenProctoringAPIBinding {
final String description = "This SEB access was auto-generated by SEB Server";
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(SPS_API.SEB_ACCESS_ENDPOINT)
.build()
.toUriString();
@@ -816,7 +819,7 @@ class ScreenProctoringAPIBinding {
try {
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(domainPath)
.pathSegment(uuid)
.pathSegment(activate ? SPS_API.ACTIVE_PATH_SEGMENT : SPS_API.INACTIVE_PATH_SEGMENT)
@@ -840,7 +843,7 @@ class ScreenProctoringAPIBinding {
try {
final String uri = UriComponentsBuilder
- .fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
+ .fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
.path(domainPath)
.pathSegment(uuid)
.build()
diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java
index 7e47c800..2ccf8d5d 100644
--- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java
+++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java
@@ -14,6 +14,7 @@ import java.util.List;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
+import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
@@ -281,4 +282,14 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
}
+ @ExceptionHandler(ClientAbortException.class)
+ public ResponseEntity