SEBSERV-435 finished up bundle and auto login
This commit is contained in:
parent
6e5d5e7710
commit
2af659dd33
23 changed files with 336 additions and 252 deletions
|
@ -56,6 +56,7 @@ public final class API {
|
||||||
|
|
||||||
public static final String OAUTH_ENDPOINT = "/oauth";
|
public static final String OAUTH_ENDPOINT = "/oauth";
|
||||||
public static final String OAUTH_TOKEN_ENDPOINT = OAUTH_ENDPOINT + "/token";
|
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 OAUTH_REVOKE_TOKEN_ENDPOINT = OAUTH_ENDPOINT + "/revoke-token";
|
||||||
|
|
||||||
public static final String CURRENT_USER_PATH_SEGMENT = "/me";
|
public static final String CURRENT_USER_PATH_SEGMENT = "/me";
|
||||||
|
|
|
@ -34,6 +34,8 @@ public class ScreenProctoringSettings {
|
||||||
public static final String ATTR_SPS_ACCOUNT_ID = "spsAccountId";
|
public static final String ATTR_SPS_ACCOUNT_ID = "spsAccountId";
|
||||||
public static final String ATTR_SPS_ACCOUNT_PASSWORD = "spsAccountPassword";
|
public static final String ATTR_SPS_ACCOUNT_PASSWORD = "spsAccountPassword";
|
||||||
|
|
||||||
|
public static final String ATTR_SPS_BUNDLED = "bundled";
|
||||||
|
|
||||||
@JsonProperty(Domain.EXAM.ATTR_ID)
|
@JsonProperty(Domain.EXAM.ATTR_ID)
|
||||||
public final Long examId;
|
public final Long examId;
|
||||||
|
|
||||||
|
@ -62,6 +64,9 @@ public class ScreenProctoringSettings {
|
||||||
@JsonProperty(ATTR_COLLECTING_GROUP_SIZE)
|
@JsonProperty(ATTR_COLLECTING_GROUP_SIZE)
|
||||||
public final Integer collectingGroupSize;
|
public final Integer collectingGroupSize;
|
||||||
|
|
||||||
|
@JsonProperty(ATTR_SPS_BUNDLED)
|
||||||
|
public final boolean bundled;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public ScreenProctoringSettings(
|
public ScreenProctoringSettings(
|
||||||
@JsonProperty(Domain.EXAM.ATTR_ID) final Long examId,
|
@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_ID) final String spsAccountId,
|
||||||
@JsonProperty(ATTR_SPS_ACCOUNT_PASSWORD) final CharSequence spsAccountPassword,
|
@JsonProperty(ATTR_SPS_ACCOUNT_PASSWORD) final CharSequence spsAccountPassword,
|
||||||
@JsonProperty(ATTR_COLLECTING_STRATEGY) final CollectingStrategy collectingStrategy,
|
@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.examId = examId;
|
||||||
this.enableScreenProctoring = enableScreenProctoring;
|
this.enableScreenProctoring = enableScreenProctoring;
|
||||||
|
@ -83,6 +89,30 @@ public class ScreenProctoringSettings {
|
||||||
this.spsAccountPassword = spsAccountPassword;
|
this.spsAccountPassword = spsAccountPassword;
|
||||||
this.collectingStrategy = collectingStrategy;
|
this.collectingStrategy = collectingStrategy;
|
||||||
this.collectingGroupSize = collectingGroupSize;
|
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) {
|
public ScreenProctoringSettings(final Exam exam) {
|
||||||
|
@ -108,6 +138,7 @@ public class ScreenProctoringSettings {
|
||||||
this.collectingGroupSize = Integer.parseInt(exam.additionalAttributes.getOrDefault(
|
this.collectingGroupSize = Integer.parseInt(exam.additionalAttributes.getOrDefault(
|
||||||
ATTR_COLLECTING_GROUP_SIZE,
|
ATTR_COLLECTING_GROUP_SIZE,
|
||||||
"-1"));
|
"-1"));
|
||||||
|
this.bundled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getExamId() {
|
public Long getExamId() {
|
||||||
|
@ -151,6 +182,10 @@ public class ScreenProctoringSettings {
|
||||||
return Objects.hash(this.examId);
|
return Objects.hash(this.examId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBundled() {
|
||||||
|
return this.bundled;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
|
|
|
@ -39,6 +39,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.codec.binary.Hex;
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -919,4 +920,12 @@ public final class Utils {
|
||||||
.replaceAll("[^A-Za-z0-9_]", "");
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
@ -26,21 +25,17 @@ import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
|
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ScreenProctoringWindowData;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringWindowScriptResolver;
|
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringWindowScriptResolver;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
public class ProctoringServlet extends HttpServlet {
|
public class ProctoringServlet extends HttpServlet {
|
||||||
|
|
||||||
public static final String SCREEN_PROCOTRING_FLAG_PARAM = "screenproctoring";
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 3475978419653411800L;
|
private static final long serialVersionUID = 3475978419653411800L;
|
||||||
private static final Logger log = LoggerFactory.getLogger(ProctoringServlet.class);
|
private static final Logger log = LoggerFactory.getLogger(ProctoringServlet.class);
|
||||||
|
|
||||||
|
@ -63,54 +58,12 @@ public class ProctoringServlet extends HttpServlet {
|
||||||
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
||||||
.getRequiredWebApplicationContext(servletContext);
|
.getRequiredWebApplicationContext(servletContext);
|
||||||
|
|
||||||
UserInfo user;
|
final boolean authenticated = isAuthenticated(httpSession, webApplicationContext);
|
||||||
try {
|
if (!authenticated) {
|
||||||
user = isAuthenticated(httpSession, webApplicationContext);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
resp.setStatus(HttpStatus.FORBIDDEN.value());
|
resp.setStatus(HttpStatus.FORBIDDEN.value());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String parameter = req.getParameter(SCREEN_PROCOTRING_FLAG_PARAM);
|
|
||||||
if (BooleanUtils.toBoolean(parameter)) {
|
|
||||||
openScreenProctoring(req, resp, user, httpSession);
|
|
||||||
} else {
|
|
||||||
openRemoteProctoring(resp, httpSession);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openScreenProctoring(
|
|
||||||
final HttpServletRequest req,
|
|
||||||
final HttpServletResponse resp,
|
|
||||||
final UserInfo user,
|
|
||||||
final HttpSession httpSession) throws IOException {
|
|
||||||
|
|
||||||
final ScreenProctoringWindowData data = (ScreenProctoringWindowData) httpSession
|
|
||||||
.getAttribute(ProctoringGUIService.SESSION_ATTR_SCREEN_PROCTORING_DATA);
|
|
||||||
|
|
||||||
// NOTE: POST on data.loginLocation seems not to work for automated login
|
|
||||||
// TODO discuss with Nadim how to make a direct login POST on the GUI client
|
|
||||||
// maybe there is a way to expose /login endpoint for directly POST credentials for login.
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/46582/response-redirect-with-post-instead-of-get
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
// sb.append("<html>");
|
|
||||||
// sb.append("<body onload='document.forms[\"form\"].submit()'>");
|
|
||||||
// sb.append("<form name='form' action='");
|
|
||||||
// sb.append( "" /* data.loginLocation */).append("' method='post'>");
|
|
||||||
// sb.append("</input type='hidden' name='username' value='").append("super-admin").append("'>");
|
|
||||||
// sb.append("</input type='hidden' name='password' type='password' value='").append("admin").append("'>");
|
|
||||||
// sb.append("</form>");
|
|
||||||
// sb.append("</body>");
|
|
||||||
// sb.append("</html>");
|
|
||||||
|
|
||||||
resp.getOutputStream().println(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openRemoteProctoring(
|
|
||||||
final HttpServletResponse resp,
|
|
||||||
final HttpSession httpSession) throws IOException {
|
|
||||||
|
|
||||||
final ProctoringWindowData proctoringData =
|
final ProctoringWindowData proctoringData =
|
||||||
(ProctoringWindowData) httpSession
|
(ProctoringWindowData) httpSession
|
||||||
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
|
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
|
||||||
|
@ -136,7 +89,7 @@ public class ProctoringServlet extends HttpServlet {
|
||||||
resp.setStatus(HttpServletResponse.SC_OK);
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserInfo isAuthenticated(
|
private boolean isAuthenticated(
|
||||||
final HttpSession httpSession,
|
final HttpSession httpSession,
|
||||||
final WebApplicationContext webApplicationContext) {
|
final WebApplicationContext webApplicationContext) {
|
||||||
|
|
||||||
|
@ -144,11 +97,7 @@ public class ProctoringServlet extends HttpServlet {
|
||||||
.getBean(AuthorizationContextHolder.class);
|
.getBean(AuthorizationContextHolder.class);
|
||||||
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
||||||
.getAuthorizationContext(httpSession);
|
.getAuthorizationContext(httpSession);
|
||||||
if (!authorizationContext.isValid() || !authorizationContext.isLoggedIn()) {
|
return authorizationContext.isValid() && authorizationContext.isLoggedIn();
|
||||||
throw new RuntimeException("No authentication found");
|
|
||||||
}
|
|
||||||
|
|
||||||
return authorizationContext.getLoggedInUser().getOrThrow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -199,7 +199,13 @@ public class ScreenProctoringSettingsPopup {
|
||||||
new ActionEvent(action),
|
new ActionEvent(action),
|
||||||
action.pageContext());
|
action.pageContext());
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,11 +249,14 @@ public class ScreenProctoringSettingsPopup {
|
||||||
this.pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
|
this.pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
|
||||||
|
|
||||||
final FormHandle<Entity> form = this.pageService.formBuilder(formContext)
|
final FormHandle<Entity> form = this.pageService.formBuilder(formContext)
|
||||||
|
.putStaticValueIf(
|
||||||
|
() -> settings.bundled,
|
||||||
|
ScreenProctoringSettings.ATTR_SPS_BUNDLED,
|
||||||
|
Constants.TRUE_STRING)
|
||||||
.withDefaultSpanInput(5)
|
.withDefaultSpanInput(5)
|
||||||
.withEmptyCellSeparation(true)
|
.withEmptyCellSeparation(true)
|
||||||
.withDefaultSpanEmptyCell(1)
|
.withDefaultSpanEmptyCell(1)
|
||||||
.readonly(isReadonly)
|
.readonly(isReadonly)
|
||||||
|
|
||||||
.addField(FormBuilder.text(
|
.addField(FormBuilder.text(
|
||||||
"Info",
|
"Info",
|
||||||
FORM_INFO_TITLE,
|
FORM_INFO_TITLE,
|
||||||
|
@ -265,33 +274,39 @@ public class ScreenProctoringSettingsPopup {
|
||||||
ScreenProctoringSettings.ATTR_SPS_SERVICE_URL,
|
ScreenProctoringSettings.ATTR_SPS_SERVICE_URL,
|
||||||
FORM_URL,
|
FORM_URL,
|
||||||
settings.spsServiceURL)
|
settings.spsServiceURL)
|
||||||
.mandatory())
|
.mandatory()
|
||||||
|
.readonly(settings.bundled))
|
||||||
|
|
||||||
.addField(FormBuilder.text(
|
.addField(FormBuilder.text(
|
||||||
ScreenProctoringSettings.ATTR_SPS_API_KEY,
|
ScreenProctoringSettings.ATTR_SPS_API_KEY,
|
||||||
FORM_APPKEY_SPS,
|
FORM_APPKEY_SPS,
|
||||||
settings.spsAPIKey))
|
settings.spsAPIKey)
|
||||||
|
.readonly(settings.bundled))
|
||||||
.withEmptyCellSeparation(false)
|
.withEmptyCellSeparation(false)
|
||||||
|
|
||||||
.addField(FormBuilder.password(
|
.addFieldIf(
|
||||||
ScreenProctoringSettings.ATTR_SPS_API_SECRET,
|
() -> !settings.bundled,
|
||||||
FORM_APPSECRET_SPS,
|
() -> FormBuilder.password(
|
||||||
(settings.spsAPISecret != null)
|
ScreenProctoringSettings.ATTR_SPS_API_SECRET,
|
||||||
? String.valueOf(settings.spsAPISecret)
|
FORM_APPSECRET_SPS,
|
||||||
: null))
|
(settings.spsAPISecret != null)
|
||||||
|
? String.valueOf(settings.spsAPISecret)
|
||||||
|
: null))
|
||||||
|
|
||||||
.addField(FormBuilder.text(
|
.addField(FormBuilder.text(
|
||||||
ScreenProctoringSettings.ATTR_SPS_ACCOUNT_ID,
|
ScreenProctoringSettings.ATTR_SPS_ACCOUNT_ID,
|
||||||
FORM_ACCOUNT_ID_SPS,
|
FORM_ACCOUNT_ID_SPS,
|
||||||
settings.spsAccountId))
|
settings.spsAccountId)
|
||||||
|
.readonly(settings.bundled))
|
||||||
.withEmptyCellSeparation(false)
|
.withEmptyCellSeparation(false)
|
||||||
|
.addFieldIf(
|
||||||
.addField(FormBuilder.password(
|
() -> !settings.bundled,
|
||||||
ScreenProctoringSettings.ATTR_SPS_ACCOUNT_PASSWORD,
|
() -> FormBuilder.password(
|
||||||
FORM_ACCOUNT_SECRET_SPS,
|
ScreenProctoringSettings.ATTR_SPS_ACCOUNT_PASSWORD,
|
||||||
(settings.spsAccountPassword != null)
|
FORM_ACCOUNT_SECRET_SPS,
|
||||||
? String.valueOf(settings.spsAccountPassword)
|
(settings.spsAccountPassword != null)
|
||||||
: null))
|
? String.valueOf(settings.spsAccountPassword)
|
||||||
|
: null))
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
public void addToGroup(final String groupName, final String fieldName) {
|
||||||
if (this.formFields.containsKey(fieldName)) {
|
if (this.formFields.containsKey(fieldName)) {
|
||||||
this.groups.computeIfAbsent(groupName, k -> new HashSet<>())
|
this.groups.computeIfAbsent(groupName, k -> new HashSet<>())
|
||||||
|
|
|
@ -212,6 +212,14 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getUserPassword() {
|
||||||
|
if (isLoggedIn()) {
|
||||||
|
return this.resource.getPassword();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean login(final String username, final CharSequence password) {
|
public boolean login(final String username, final CharSequence password) {
|
||||||
if (!this.valid || this.isLoggedIn()) {
|
if (!this.valid || this.isLoggedIn()) {
|
||||||
|
@ -363,6 +371,5 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,4 +63,6 @@ public interface SEBServerAuthorizationContext {
|
||||||
* @return the underling RestTemplate to connect and communicate with the SEB Server webservice */
|
* @return the underling RestTemplate to connect and communicate with the SEB Server webservice */
|
||||||
RestTemplate getRestTemplate();
|
RestTemplate getRestTemplate();
|
||||||
|
|
||||||
|
CharSequence getUserPassword();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.core.io.Resource;
|
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.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.client.RestTemplate;
|
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.RemoteProctoringRoom;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ScreenProctoringGroup;
|
import ch.ethz.seb.sebserver.gbl.model.session.ScreenProctoringGroup;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
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.Tuple;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
|
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
|
||||||
import ch.ethz.seb.sebserver.gui.ProctoringServlet;
|
|
||||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||||
import ch.ethz.seb.sebserver.gui.content.action.ActionPane;
|
import ch.ethz.seb.sebserver.gui.content.action.ActionPane;
|
||||||
import ch.ethz.seb.sebserver.gui.content.monitoring.ProctorRoomConnectionsPopup;
|
import ch.ethz.seb.sebserver.gui.content.monitoring.ProctorRoomConnectionsPopup;
|
||||||
|
@ -98,16 +101,16 @@ public class MonitoringProctoringService {
|
||||||
private final JSONMapper jsonMapper;
|
private final JSONMapper jsonMapper;
|
||||||
private final Resource openRoomScriptRes;
|
private final Resource openRoomScriptRes;
|
||||||
private final String remoteProctoringEndpoint;
|
private final String remoteProctoringEndpoint;
|
||||||
private final String remoteProctoringViewServletEndpoint;
|
private final Cryptor cryptor;
|
||||||
|
|
||||||
public MonitoringProctoringService(
|
public MonitoringProctoringService(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final GuiServiceInfo guiServiceInfo,
|
final GuiServiceInfo guiServiceInfo,
|
||||||
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
||||||
final JSONMapper jsonMapper,
|
final JSONMapper jsonMapper,
|
||||||
|
final Cryptor cryptor,
|
||||||
@Value(OPEN_ROOM_SCRIPT_RES) final Resource openRoomScript,
|
@Value(OPEN_ROOM_SCRIPT_RES) final Resource openRoomScript,
|
||||||
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint,
|
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint) {
|
||||||
@Value("${sebserver.gui.remote.proctoring.api-servler.endpoint:/remote-view-servlet}") final String remoteProctoringViewServletEndpoint) {
|
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.guiServiceInfo = guiServiceInfo;
|
this.guiServiceInfo = guiServiceInfo;
|
||||||
|
@ -115,7 +118,7 @@ public class MonitoringProctoringService {
|
||||||
this.jsonMapper = jsonMapper;
|
this.jsonMapper = jsonMapper;
|
||||||
this.openRoomScriptRes = openRoomScript;
|
this.openRoomScriptRes = openRoomScript;
|
||||||
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
||||||
this.remoteProctoringViewServletEndpoint = remoteProctoringViewServletEndpoint;
|
this.cryptor = cryptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTownhallRoomActive(final String examModelId) {
|
public boolean isTownhallRoomActive(final String examModelId) {
|
||||||
|
@ -314,34 +317,55 @@ public class MonitoringProctoringService {
|
||||||
final ScreenProctoringGroup group,
|
final ScreenProctoringGroup group,
|
||||||
final PageAction _action) {
|
final PageAction _action) {
|
||||||
|
|
||||||
// TODO make this configurable or static
|
try {
|
||||||
final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location";
|
// Get login Token for user login from SPS service
|
||||||
final ResponseEntity<String> redirect = new RestTemplate().exchange(
|
final RestTemplate restTemplate = new RestTemplate();
|
||||||
serviceRedirect,
|
final String serviceRedirect = settings.spsServiceURL + "/gui-redirect-location";
|
||||||
HttpMethod.GET,
|
final ResponseEntity<String> redirect = restTemplate.exchange(
|
||||||
null,
|
serviceRedirect,
|
||||||
String.class);
|
HttpMethod.GET,
|
||||||
|
null,
|
||||||
|
String.class);
|
||||||
|
|
||||||
final String redirectLocation = redirect.getBody();
|
// JWT token request URL
|
||||||
final CurrentUser currentUser = this.pageService.getCurrentUser();
|
final String jwtTokenURL = settings.spsServiceURL + API.OAUTH_JWTTOKEN_ENDPOINT;
|
||||||
|
|
||||||
ProctoringGUIService.setCurrentScreenProctoringWindowData(
|
// Basic Auth header and content type header
|
||||||
group.uuid,
|
final HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
redirectLocation,
|
httpHeaders.add(
|
||||||
currentUser.get().username,
|
HttpHeaders.AUTHORIZATION,
|
||||||
"admin");
|
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);
|
// user credential and redirect info for jwt token request in body - form URL encoded format
|
||||||
final String url = this.guiServiceInfo.getExternalServerURIBuilder().toUriString()
|
final CurrentUser currentUser = this.pageService.getCurrentUser();
|
||||||
+ this.remoteProctoringEndpoint
|
final CharSequence userPassword = currentUser
|
||||||
+ this.remoteProctoringViewServletEndpoint
|
.getAuthorizationContextHolder()
|
||||||
+ Constants.SLASH
|
.getAuthorizationContext()
|
||||||
+ Constants.QUERY
|
.getUserPassword();
|
||||||
+ ProctoringServlet.SCREEN_PROCOTRING_FLAG_PARAM
|
final String body = "username=" + currentUser.get().username
|
||||||
+ Constants.EQUALITY_SIGN
|
+ "&password=" + userPassword.toString()
|
||||||
+ Constants.TRUE_STRING;
|
+ "&redirect=/galleryView/" + group.uuid;
|
||||||
|
|
||||||
launcher.openURL(url);
|
// apply jwt token request
|
||||||
|
final HttpEntity<String> httpEntity = new HttpEntity<>(body, httpHeaders);
|
||||||
|
final ResponseEntity<String> 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;
|
return _action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
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;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.WebserviceInfoDAO;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -80,9 +81,12 @@ public class WebserviceInfo {
|
||||||
@Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}")
|
@Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}")
|
||||||
private int examAPITokenValiditySeconds;
|
private int examAPITokenValiditySeconds;
|
||||||
|
|
||||||
|
private final ScreenProctoringServiceBundle screenProctoringServiceBundle;
|
||||||
|
|
||||||
public WebserviceInfo(
|
public WebserviceInfo(
|
||||||
final WebserviceInfoDAO webserviceInfoDAO,
|
final WebserviceInfoDAO webserviceInfoDAO,
|
||||||
final Environment environment) {
|
final Environment environment,
|
||||||
|
final Cryptor cryptor) {
|
||||||
|
|
||||||
this.webserviceInfoDAO = webserviceInfoDAO;
|
this.webserviceInfoDAO = webserviceInfoDAO;
|
||||||
this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY);
|
this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY);
|
||||||
|
@ -145,6 +149,24 @@ public class WebserviceInfo {
|
||||||
} else {
|
} else {
|
||||||
this.lmsExternalAddressAlias = Collections.emptyMap();
|
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() {
|
public boolean isMaster() {
|
||||||
|
@ -207,6 +229,10 @@ public class WebserviceInfo {
|
||||||
return this.distributedUpdateInterval;
|
return this.distributedUpdateInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ScreenProctoringServiceBundle getScreenProctoringServiceBundle() {
|
||||||
|
return this.screenProctoringServiceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
public String getLocalHostName() {
|
public String getLocalHostName() {
|
||||||
try {
|
try {
|
||||||
return InetAddress.getLocalHost().getHostName();
|
return InetAddress.getLocalHost().getHostName();
|
||||||
|
@ -300,4 +326,53 @@ public class WebserviceInfo {
|
||||||
return builder.toString();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ import org.springframework.stereotype.Component;
|
||||||
import ch.ethz.seb.sebserver.SEBServerInit;
|
import ch.ethz.seb.sebserver.SEBServerInit;
|
||||||
import ch.ethz.seb.sebserver.SEBServerInitEvent;
|
import ch.ethz.seb.sebserver.SEBServerInitEvent;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
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.dao.WebserviceInfoDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.SEBClientPingServiceFactory;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ -39,7 +39,6 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
||||||
private final WebserviceInfoDAO webserviceInfoDAO;
|
private final WebserviceInfoDAO webserviceInfoDAO;
|
||||||
private final DBIntegrityChecker dbIntegrityChecker;
|
private final DBIntegrityChecker dbIntegrityChecker;
|
||||||
private final SEBServerMigrationStrategy sebServerMigrationStrategy;
|
private final SEBServerMigrationStrategy sebServerMigrationStrategy;
|
||||||
private final SEBClientPingServiceFactory sebClientPingServiceFactory;
|
|
||||||
|
|
||||||
protected WebserviceInit(
|
protected WebserviceInit(
|
||||||
final SEBServerInit sebServerInit,
|
final SEBServerInit sebServerInit,
|
||||||
|
@ -49,8 +48,7 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
||||||
final WebserviceInfoDAO webserviceInfoDAO,
|
final WebserviceInfoDAO webserviceInfoDAO,
|
||||||
final DBIntegrityChecker dbIntegrityChecker,
|
final DBIntegrityChecker dbIntegrityChecker,
|
||||||
final ApplicationContext applicationContext,
|
final ApplicationContext applicationContext,
|
||||||
final SEBServerMigrationStrategy sebServerMigrationStrategy,
|
final SEBServerMigrationStrategy sebServerMigrationStrategy) {
|
||||||
final SEBClientPingServiceFactory sebClientPingServiceFactory) {
|
|
||||||
|
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.sebServerInit = sebServerInit;
|
this.sebServerInit = sebServerInit;
|
||||||
|
@ -61,7 +59,6 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
||||||
this.webserviceInfoDAO = webserviceInfoDAO;
|
this.webserviceInfoDAO = webserviceInfoDAO;
|
||||||
this.dbIntegrityChecker = dbIntegrityChecker;
|
this.dbIntegrityChecker = dbIntegrityChecker;
|
||||||
this.sebServerMigrationStrategy = sebServerMigrationStrategy;
|
this.sebServerMigrationStrategy = sebServerMigrationStrategy;
|
||||||
this.sebClientPingServiceFactory = sebClientPingServiceFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationContext getApplicationContext() {
|
public ApplicationContext getApplicationContext() {
|
||||||
|
@ -126,7 +123,7 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
||||||
|
|
||||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||||
SEBServerInit.INIT_LOGGER.info("----> Working with ping service: {}",
|
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("----> ");
|
||||||
SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
|
SEBServerInit.INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
|
||||||
|
@ -153,6 +150,14 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
||||||
"----> admin API refresh token validity: " + this.webserviceInfo.getAdminRefreshTokenValSec() + "s");
|
"----> admin API refresh token validity: " + this.webserviceInfo.getAdminRefreshTokenValSec() + "s");
|
||||||
SEBServerInit.INIT_LOGGER.info(
|
SEBServerInit.INIT_LOGGER.info(
|
||||||
"----> exam API access token validity: " + this.webserviceInfo.getExamAPITokenValiditySeconds() + "s");
|
"----> 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("----> ");
|
||||||
SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
|
SEBServerInit.INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public interface ProctoringSettingsDAO {
|
||||||
EntityKey entityKey,
|
EntityKey entityKey,
|
||||||
ProctoringServiceSettings proctoringServiceSettings);
|
ProctoringServiceSettings proctoringServiceSettings);
|
||||||
|
|
||||||
Result<ScreenProctoringSettings> getScreenProctoringSettings(EntityKey entityKey);
|
Result<ScreenProctoringSettings> getScreenProctoringSettings(EntityKey entityKeyp);
|
||||||
|
|
||||||
Result<ScreenProctoringSettings> storeScreenProctoringSettings(
|
Result<ScreenProctoringSettings> storeScreenProctoringSettings(
|
||||||
final EntityKey entityKey,
|
final EntityKey entityKey,
|
||||||
|
|
|
@ -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.ProctoringServiceSettings.ProctoringServerType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
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.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.ProctoringSettingsDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ProctoringSettingsDAOImpl;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ProctoringSettingsDAOImpl;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
||||||
|
@ -36,17 +39,23 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
||||||
private final RemoteProctoringServiceFactory remoteProctoringServiceFactory;
|
private final RemoteProctoringServiceFactory remoteProctoringServiceFactory;
|
||||||
private final ScreenProctoringService screenProctoringService;
|
private final ScreenProctoringService screenProctoringService;
|
||||||
private final ExamSessionCacheService examSessionCacheService;
|
private final ExamSessionCacheService examSessionCacheService;
|
||||||
|
private final ScreenProctoringServiceBundle screenProctoringServiceBundle;
|
||||||
|
private final Cryptor cryptor;
|
||||||
|
|
||||||
public ProctoringAdminServiceImpl(
|
public ProctoringAdminServiceImpl(
|
||||||
final ProctoringSettingsDAOImpl proctoringSettingsDAO,
|
final ProctoringSettingsDAOImpl proctoringSettingsDAO,
|
||||||
final RemoteProctoringServiceFactory remoteProctoringServiceFactory,
|
final RemoteProctoringServiceFactory remoteProctoringServiceFactory,
|
||||||
final ScreenProctoringService screenProctoringService,
|
final ScreenProctoringService screenProctoringService,
|
||||||
final ExamSessionCacheService examSessionCacheService) {
|
final ExamSessionCacheService examSessionCacheService,
|
||||||
|
final WebserviceInfo webserviceInfo,
|
||||||
|
final Cryptor cryptor) {
|
||||||
|
|
||||||
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
||||||
this.remoteProctoringServiceFactory = remoteProctoringServiceFactory;
|
this.remoteProctoringServiceFactory = remoteProctoringServiceFactory;
|
||||||
this.screenProctoringService = screenProctoringService;
|
this.screenProctoringService = screenProctoringService;
|
||||||
this.examSessionCacheService = examSessionCacheService;
|
this.examSessionCacheService = examSessionCacheService;
|
||||||
|
this.screenProctoringServiceBundle = webserviceInfo.getScreenProctoringServiceBundle();
|
||||||
|
this.cryptor = cryptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,9 +100,25 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
||||||
|
|
||||||
checkType(parentEntityKey);
|
checkType(parentEntityKey);
|
||||||
|
|
||||||
return this.proctoringSettingsDAO
|
ScreenProctoringSettings settings = this.proctoringSettingsDAO
|
||||||
.getScreenProctoringSettings(parentEntityKey)
|
.getScreenProctoringSettings(parentEntityKey)
|
||||||
.getOrThrow();
|
.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);
|
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
|
this.screenProctoringService
|
||||||
.testSettings(screenProctoringSettings)
|
.testSettings(settings)
|
||||||
.flatMap(settings -> this.proctoringSettingsDAO.storeScreenProctoringSettings(
|
.flatMap(s -> this.proctoringSettingsDAO.storeScreenProctoringSettings(parentEntityKey, s))
|
||||||
parentEntityKey,
|
|
||||||
screenProctoringSettings))
|
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
if (parentEntityKey.entityType == EntityType.EXAM) {
|
if (parentEntityKey.entityType == EntityType.EXAM) {
|
||||||
|
|
||||||
this.screenProctoringService
|
this.screenProctoringService
|
||||||
.applyScreenProctoingForExam(screenProctoringSettings.examId)
|
.applyScreenProctoingForExam(settings.examId)
|
||||||
.onError(error -> this.proctoringSettingsDAO
|
.onError(error -> this.proctoringSettingsDAO
|
||||||
.disableScreenProctoring(screenProctoringSettings.examId))
|
.disableScreenProctoring(screenProctoringSettings.examId))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
@ -128,7 +166,7 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return screenProctoringSettings;
|
return settings;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.ehcache.impl.internal.concurrent.ConcurrentHashMap;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -33,6 +34,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientPingServic
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ConditionalOnExpression("'${sebserver.webservice.ping.service.strategy}'.equals('BATCH')")
|
||||||
public class SEBClientPingBatchService implements SEBClientPingService {
|
public class SEBClientPingBatchService implements SEBClientPingService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SEBClientPingBatchService.class);
|
private static final Logger log = LoggerFactory.getLogger(SEBClientPingBatchService.class);
|
||||||
|
@ -116,9 +118,9 @@ public class SEBClientPingBatchService implements SEBClientPingService {
|
||||||
+ this.instructions);
|
+ this.instructions);
|
||||||
this.pings.put(connectionToken, instructionConfirm);
|
this.pings.put(connectionToken, instructionConfirm);
|
||||||
// // TODO is this a good idea or is there another better way to deal with instruction confirm synchronization?
|
// // 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 + "\"")) {
|
if (instruction != null && instruction.contains("\"instruction-confirm\":\"" + instructionConfirm + "\"")) {
|
||||||
// return null;
|
return null;
|
||||||
// }
|
}
|
||||||
} else if (!this.pings.containsKey(connectionToken)) {
|
} else if (!this.pings.containsKey(connectionToken)) {
|
||||||
this.pings.put(connectionToken, StringUtils.EMPTY);
|
this.pings.put(connectionToken, StringUtils.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientPingServic
|
||||||
@Lazy
|
@Lazy
|
||||||
@Component
|
@Component
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ConditionalOnExpression("'${sebserver.webservice.ping.service.strategy}'.equals('BLOCKING')")
|
||||||
public class SEBClientPingBlockingService implements SEBClientPingService {
|
public class SEBClientPingBlockingService implements SEBClientPingService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SEBClientPingBlockingService.class);
|
private static final Logger log = LoggerFactory.getLogger(SEBClientPingBlockingService.class);
|
||||||
|
|
|
@ -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<SEBClientPingService.PingServiceType, SEBClientPingService> serviceMapping =
|
|
||||||
new EnumMap<>(SEBClientPingService.PingServiceType.class);
|
|
||||||
private final SEBClientPingService.PingServiceType workingServiceType;
|
|
||||||
|
|
||||||
public SEBClientPingServiceFactory(
|
|
||||||
final Collection<SEBClientPingService> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -60,7 +60,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
|
||||||
final InternalClientConnectionDataFactory internalClientConnectionDataFactory,
|
final InternalClientConnectionDataFactory internalClientConnectionDataFactory,
|
||||||
final SecurityKeyService securityKeyService,
|
final SecurityKeyService securityKeyService,
|
||||||
final SEBClientVersionService sebClientVersionService,
|
final SEBClientVersionService sebClientVersionService,
|
||||||
final SEBClientPingServiceFactory sebClientPingServiceFactory) {
|
final SEBClientPingService sebClientPingService) {
|
||||||
|
|
||||||
this.clientConnectionDAO = clientConnectionDAO;
|
this.clientConnectionDAO = clientConnectionDAO;
|
||||||
this.examSessionService = examSessionService;
|
this.examSessionService = examSessionService;
|
||||||
|
@ -70,7 +70,7 @@ public class SEBClientSessionServiceImpl implements SEBClientSessionService {
|
||||||
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
|
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
|
||||||
this.securityKeyService = securityKeyService;
|
this.securityKeyService = securityKeyService;
|
||||||
this.sebClientVersionService = sebClientVersionService;
|
this.sebClientVersionService = sebClientVersionService;
|
||||||
this.sebClientPingService = sebClientPingServiceFactory.getSEBClientPingService();
|
this.sebClientPingService = sebClientPingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -223,6 +223,10 @@ class ScreenProctoringAPIBinding {
|
||||||
|
|
||||||
if (result.getStatusCode() != HttpStatus.OK) {
|
if (result.getStatusCode() != HttpStatus.OK) {
|
||||||
if (result.getStatusCode().is4xxClientError()) {
|
if (result.getStatusCode().is4xxClientError()) {
|
||||||
|
log.warn(
|
||||||
|
"Failed to establish REST connection to: {}. status: {}",
|
||||||
|
screenProctoringSettings.spsServiceURL, result.getStatusCode());
|
||||||
|
|
||||||
throw new FieldValidationException(
|
throw new FieldValidationException(
|
||||||
"serverURL",
|
"serverURL",
|
||||||
"screenProctoringSettings:spsServiceURL:url.noAccess");
|
"screenProctoringSettings:spsServiceURL:url.noAccess");
|
||||||
|
@ -302,7 +306,6 @@ class ScreenProctoringAPIBinding {
|
||||||
final SPSData spsData = this.getSPSData(exam.id);
|
final SPSData spsData = this.getSPSData(exam.id);
|
||||||
// re-activate all needed entities on SPS side
|
// re-activate all needed entities on SPS side
|
||||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, true, apiTemplate);
|
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
|
// mark successfully activated on SPS side
|
||||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||||
|
@ -367,7 +370,7 @@ class ScreenProctoringAPIBinding {
|
||||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.EXAM_ENDPOINT)
|
.path(SPS_API.EXAM_ENDPOINT)
|
||||||
.pathSegment(spsData.spsExamUUID)
|
.pathSegment(spsData.spsExamUUID)
|
||||||
.build()
|
.build()
|
||||||
|
@ -410,8 +413,8 @@ class ScreenProctoringAPIBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
final SPSData spsData = this.getSPSData(exam.id);
|
final SPSData spsData = this.getSPSData(exam.id);
|
||||||
activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, this.apiTemplate);
|
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, false, this.apiTemplate);
|
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, false, apiTemplate);
|
||||||
|
|
||||||
// mark successfully dispose on SPS side
|
// mark successfully dispose on SPS side
|
||||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||||
|
@ -490,7 +493,7 @@ class ScreenProctoringAPIBinding {
|
||||||
final String token = clientConnection.getConnectionToken();
|
final String token = clientConnection.getConnectionToken();
|
||||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(examId);
|
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(examId);
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.SESSION_ENDPOINT)
|
.path(SPS_API.SESSION_ENDPOINT)
|
||||||
|
|
||||||
.build()
|
.build()
|
||||||
|
@ -567,7 +570,7 @@ class ScreenProctoringAPIBinding {
|
||||||
userInfo.roles);
|
userInfo.roles);
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.USERSYNC_SEBSERVER_ENDPOINT)
|
.path(SPS_API.USERSYNC_SEBSERVER_ENDPOINT)
|
||||||
.build()
|
.build()
|
||||||
.toUriString();
|
.toUriString();
|
||||||
|
@ -603,7 +606,7 @@ class ScreenProctoringAPIBinding {
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.ENTIY_PRIVILEGES_ENDPOINT)
|
.path(SPS_API.ENTIY_PRIVILEGES_ENDPOINT)
|
||||||
.build()
|
.build()
|
||||||
.toUriString();
|
.toUriString();
|
||||||
|
@ -696,7 +699,7 @@ class ScreenProctoringAPIBinding {
|
||||||
throws JsonMappingException, JsonProcessingException {
|
throws JsonMappingException, JsonProcessingException {
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.GROUP_ENDPOINT)
|
.path(SPS_API.GROUP_ENDPOINT)
|
||||||
.build()
|
.build()
|
||||||
.toUriString();
|
.toUriString();
|
||||||
|
@ -729,7 +732,7 @@ class ScreenProctoringAPIBinding {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.EXAM_ENDPOINT)
|
.path(SPS_API.EXAM_ENDPOINT)
|
||||||
.build().toUriString();
|
.build().toUriString();
|
||||||
|
|
||||||
|
@ -773,7 +776,7 @@ class ScreenProctoringAPIBinding {
|
||||||
final String description = "This SEB access was auto-generated by SEB Server";
|
final String description = "This SEB access was auto-generated by SEB Server";
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(SPS_API.SEB_ACCESS_ENDPOINT)
|
.path(SPS_API.SEB_ACCESS_ENDPOINT)
|
||||||
.build()
|
.build()
|
||||||
.toUriString();
|
.toUriString();
|
||||||
|
@ -816,7 +819,7 @@ class ScreenProctoringAPIBinding {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(domainPath)
|
.path(domainPath)
|
||||||
.pathSegment(uuid)
|
.pathSegment(uuid)
|
||||||
.pathSegment(activate ? SPS_API.ACTIVE_PATH_SEGMENT : SPS_API.INACTIVE_PATH_SEGMENT)
|
.pathSegment(activate ? SPS_API.ACTIVE_PATH_SEGMENT : SPS_API.INACTIVE_PATH_SEGMENT)
|
||||||
|
@ -840,7 +843,7 @@ class ScreenProctoringAPIBinding {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
final String uri = UriComponentsBuilder
|
final String uri = UriComponentsBuilder
|
||||||
.fromUriString(this.apiTemplate.screenProctoringSettings.spsServiceURL)
|
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||||
.path(domainPath)
|
.path(domainPath)
|
||||||
.pathSegment(uuid)
|
.pathSegment(uuid)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.catalina.connector.ClientAbortException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
@ -281,4 +282,14 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(ClientAbortException.class)
|
||||||
|
public ResponseEntity<Object> handleClientAbortException(
|
||||||
|
final ClientAbortException ex,
|
||||||
|
final WebRequest request) {
|
||||||
|
|
||||||
|
log.warn("Client aborted: {}", ex.getMessage());
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 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.weblayer.api;
|
|
||||||
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
//@EnableAsync
|
|
||||||
//@Configuration
|
|
||||||
//@WebServiceProfile
|
|
||||||
@Deprecated
|
|
||||||
public class ControllerConfig implements WebMvcConfigurer {
|
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
|
|
||||||
// configurer.setTaskExecutor(threadPoolTaskExecutor());
|
|
||||||
// configurer.setDefaultTimeout(30000);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public AsyncTaskExecutor threadPoolTaskExecutor() {
|
|
||||||
// final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
||||||
// executor.setCorePoolSize(7);
|
|
||||||
// executor.setMaxPoolSize(42);
|
|
||||||
// executor.setQueueCapacity(11);
|
|
||||||
// executor.setThreadNamePrefix("mvc-");
|
|
||||||
// executor.initialize();
|
|
||||||
// return executor;
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
|
@ -23,7 +23,7 @@ sebserver.webservice.distributed.updateInterval=1000
|
||||||
sebserver.webservice.distributed.connectionUpdate=2000
|
sebserver.webservice.distributed.connectionUpdate=2000
|
||||||
sebserver.webservice.clean-db-on-startup=false
|
sebserver.webservice.clean-db-on-startup=false
|
||||||
|
|
||||||
# webservice configuration
|
# webservice setup configuration
|
||||||
sebserver.init.adminaccount.gen-on-init=false
|
sebserver.init.adminaccount.gen-on-init=false
|
||||||
sebserver.webservice.distributed=true
|
sebserver.webservice.distributed=true
|
||||||
#sebserver.webservice.master.delay.threshold=10000
|
#sebserver.webservice.master.delay.threshold=10000
|
||||||
|
@ -31,6 +31,7 @@ sebserver.webservice.http.external.scheme=http
|
||||||
sebserver.webservice.http.external.servername=localhost
|
sebserver.webservice.http.external.servername=localhost
|
||||||
sebserver.webservice.http.external.port=${server.port}
|
sebserver.webservice.http.external.port=${server.port}
|
||||||
sebserver.webservice.http.redirect.gui=/gui
|
sebserver.webservice.http.redirect.gui=/gui
|
||||||
|
sebserver.webservice.ping.service.strategy=BATCH
|
||||||
|
|
||||||
|
|
||||||
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
||||||
|
@ -44,8 +45,6 @@ sebserver.webservice.api.exam.time-suffix=0
|
||||||
sebserver.webservice.api.exam.endpoint=/exam-api
|
sebserver.webservice.api.exam.endpoint=/exam-api
|
||||||
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||||
sebserver.webservice.api.exam.event-handling-strategy=ASYNC_BATCH_STORE_STRATEGY
|
|
||||||
sebserver.webservice.api.exam.session.ping.service.strategy=BATCH
|
|
||||||
sebserver.webservice.api.exam.enable-indicator-cache=true
|
sebserver.webservice.api.exam.enable-indicator-cache=true
|
||||||
sebserver.webservice.api.exam.defaultPingInterval=1000
|
sebserver.webservice.api.exam.defaultPingInterval=1000
|
||||||
sebserver.webservice.api.pagination.maxPageSize=500
|
sebserver.webservice.api.pagination.maxPageSize=500
|
||||||
|
@ -62,4 +61,8 @@ springdoc.swagger-ui.enabled=true
|
||||||
management.server.port=${server.port}
|
management.server.port=${server.port}
|
||||||
management.endpoints.web.base-path=/management
|
management.endpoints.web.base-path=/management
|
||||||
management.endpoints.web.exposure.include=logfile,loggers,jolokia
|
management.endpoints.web.exposure.include=logfile,loggers,jolokia
|
||||||
management.endpoints.web.path-mapping.jolokia=jmx
|
management.endpoints.web.path-mapping.jolokia=jmx
|
||||||
|
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.url=localhost:8090
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount
|
|
@ -37,7 +37,7 @@ spring.datasource.password=${sebserver.mariadb.password}
|
||||||
sebserver.webservice.api.admin.clientSecret=${sebserver.password}
|
sebserver.webservice.api.admin.clientSecret=${sebserver.password}
|
||||||
sebserver.webservice.internalSecret=${sebserver.password}
|
sebserver.webservice.internalSecret=${sebserver.password}
|
||||||
|
|
||||||
### webservice networking
|
### webservice setup configuration
|
||||||
sebserver.webservice.forceMaster=false
|
sebserver.webservice.forceMaster=false
|
||||||
sebserver.webservice.distributed=false
|
sebserver.webservice.distributed=false
|
||||||
sebserver.webservice.distributed.updateInterval=2000
|
sebserver.webservice.distributed.updateInterval=2000
|
||||||
|
@ -45,6 +45,8 @@ sebserver.webservice.http.external.scheme=https
|
||||||
sebserver.webservice.http.external.servername=
|
sebserver.webservice.http.external.servername=
|
||||||
sebserver.webservice.http.external.port=
|
sebserver.webservice.http.external.port=
|
||||||
sebserver.webservice.http.redirect.gui=/gui
|
sebserver.webservice.http.redirect.gui=/gui
|
||||||
|
sebserver.webservice.ping.service.strategy=BLOCKING
|
||||||
|
|
||||||
|
|
||||||
### Open API Documentation
|
### Open API Documentation
|
||||||
springdoc.api-docs.enabled=false
|
springdoc.api-docs.enabled=false
|
||||||
|
@ -55,6 +57,8 @@ springdoc.swagger-ui.oauth.clientSecret=${sebserver.password}
|
||||||
#springdoc.default-consumes-media-type=application/x-www-form-urlencoded
|
#springdoc.default-consumes-media-type=application/x-www-form-urlencoded
|
||||||
springdoc.paths-to-exclude=/exam-api,/exam-api/discovery,/sebserver/error,/sebserver/check,/oauth,/exam-api/v1/*
|
springdoc.paths-to-exclude=/exam-api,/exam-api/discovery,/sebserver/error,/sebserver/check,/oauth,/exam-api/v1/*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### webservice API
|
### webservice API
|
||||||
sebserver.webservice.api.admin.clientId=guiClient
|
sebserver.webservice.api.admin.clientId=guiClient
|
||||||
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
sebserver.webservice.api.admin.endpoint=/admin-api/v1
|
||||||
|
@ -72,8 +76,7 @@ sebserver.webservice.api.exam.config.init.prohibitedProcesses=config/initialProh
|
||||||
sebserver.webservice.api.exam.endpoint=/exam-api
|
sebserver.webservice.api.exam.endpoint=/exam-api
|
||||||
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=43200
|
sebserver.webservice.api.exam.accessTokenValiditySeconds=43200
|
||||||
sebserver.webservice.api.exam.event-handling-strategy=SINGLE_EVENT_STORE_STRATEGY
|
|
||||||
sebserver.webservice.api.exam.enable-indicator-cache=true
|
sebserver.webservice.api.exam.enable-indicator-cache=true
|
||||||
sebserver.webservice.api.pagination.maxPageSize=500
|
sebserver.webservice.api.pagination.maxPageSize=500
|
||||||
# comma separated list of known possible OpenEdX API access token request endpoints
|
# comma separated list of known possible OpenEdX API access token request endpoints
|
||||||
|
|
|
@ -66,6 +66,10 @@ sebserver.ssl.redirect.html.port=8080
|
||||||
# features
|
# features
|
||||||
sebserver.feature.seb.screenProctoring=false
|
sebserver.feature.seb.screenProctoring=false
|
||||||
sebserver.feature.seb.screenProctoring.bundled=true
|
sebserver.feature.seb.screenProctoring.bundled=true
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.url=sps-service:8090
|
||||||
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
||||||
sebserver.feature.seb.screenProctoring.bundled.clientPassword=${sebserver.password}
|
sebserver.feature.seb.screenProctoring.bundled.clientPassword=${sps.sebserver.client.secret}
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.password=${sps.sebserver.password}
|
||||||
|
|
||||||
sebserver.feature.CollectingRoomStrategy.SEB-GROUP=false
|
sebserver.feature.CollectingRoomStrategy.SEB-GROUP=false
|
||||||
|
|
Loading…
Reference in a new issue