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_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";
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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("<html>");
|
||||
// sb.append("<body onload='document.forms[\"form\"].submit()'>");
|
||||
// sb.append("<form name='form' action='");
|
||||
// sb.append( "" /* data.loginLocation */).append("' method='post'>");
|
||||
// sb.append("</input type='hidden' name='username' value='").append("super-admin").append("'>");
|
||||
// sb.append("</input type='hidden' name='password' type='password' value='").append("admin").append("'>");
|
||||
// sb.append("</form>");
|
||||
// sb.append("</body>");
|
||||
// sb.append("</html>");
|
||||
|
||||
resp.getOutputStream().println(sb.toString());
|
||||
}
|
||||
|
||||
private void openRemoteProctoring(
|
||||
final HttpServletResponse resp,
|
||||
final HttpSession httpSession) throws IOException {
|
||||
|
||||
final ProctoringWindowData proctoringData =
|
||||
(ProctoringWindowData) httpSession
|
||||
.getAttribute(ProctoringGUIService.SESSION_ATTR_PROCTORING_DATA);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Entity> 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();
|
||||
|
||||
|
|
|
@ -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<>())
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,4 +63,6 @@ public interface SEBServerAuthorizationContext {
|
|||
* @return the underling RestTemplate to connect and communicate with the SEB Server webservice */
|
||||
RestTemplate getRestTemplate();
|
||||
|
||||
CharSequence getUserPassword();
|
||||
|
||||
}
|
||||
|
|
|
@ -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<String> 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<String> 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<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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<ApplicationReadyEvent
|
|||
private final WebserviceInfoDAO webserviceInfoDAO;
|
||||
private final DBIntegrityChecker dbIntegrityChecker;
|
||||
private final SEBServerMigrationStrategy sebServerMigrationStrategy;
|
||||
private final SEBClientPingServiceFactory sebClientPingServiceFactory;
|
||||
|
||||
protected WebserviceInit(
|
||||
final SEBServerInit sebServerInit,
|
||||
|
@ -49,8 +48,7 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
final WebserviceInfoDAO webserviceInfoDAO,
|
||||
final DBIntegrityChecker dbIntegrityChecker,
|
||||
final ApplicationContext applicationContext,
|
||||
final SEBServerMigrationStrategy sebServerMigrationStrategy,
|
||||
final SEBClientPingServiceFactory sebClientPingServiceFactory) {
|
||||
final SEBServerMigrationStrategy sebServerMigrationStrategy) {
|
||||
|
||||
this.applicationContext = applicationContext;
|
||||
this.sebServerInit = sebServerInit;
|
||||
|
@ -61,7 +59,6 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
this.webserviceInfoDAO = webserviceInfoDAO;
|
||||
this.dbIntegrityChecker = dbIntegrityChecker;
|
||||
this.sebServerMigrationStrategy = sebServerMigrationStrategy;
|
||||
this.sebClientPingServiceFactory = sebClientPingServiceFactory;
|
||||
}
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
|
@ -126,7 +123,7 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
|||
|
||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||
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<ApplicationReadyEvent
|
|||
"----> 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());
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public interface ProctoringSettingsDAO {
|
|||
EntityKey entityKey,
|
||||
ProctoringServiceSettings proctoringServiceSettings);
|
||||
|
||||
Result<ScreenProctoringSettings> getScreenProctoringSettings(EntityKey entityKey);
|
||||
Result<ScreenProctoringSettings> getScreenProctoringSettings(EntityKey entityKeyp);
|
||||
|
||||
Result<ScreenProctoringSettings> storeScreenProctoringSettings(
|
||||
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.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;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<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.clean-db-on-startup=false
|
||||
|
||||
# webservice configuration
|
||||
# webservice setup configuration
|
||||
sebserver.init.adminaccount.gen-on-init=false
|
||||
sebserver.webservice.distributed=true
|
||||
#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.port=${server.port}
|
||||
sebserver.webservice.http.redirect.gui=/gui
|
||||
sebserver.webservice.ping.service.strategy=BATCH
|
||||
|
||||
|
||||
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.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||
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.defaultPingInterval=1000
|
||||
sebserver.webservice.api.pagination.maxPageSize=500
|
||||
|
@ -62,4 +61,8 @@ springdoc.swagger-ui.enabled=true
|
|||
management.server.port=${server.port}
|
||||
management.endpoints.web.base-path=/management
|
||||
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.internalSecret=${sebserver.password}
|
||||
|
||||
### webservice networking
|
||||
### webservice setup configuration
|
||||
sebserver.webservice.forceMaster=false
|
||||
sebserver.webservice.distributed=false
|
||||
sebserver.webservice.distributed.updateInterval=2000
|
||||
|
@ -45,6 +45,8 @@ sebserver.webservice.http.external.scheme=https
|
|||
sebserver.webservice.http.external.servername=
|
||||
sebserver.webservice.http.external.port=
|
||||
sebserver.webservice.http.redirect.gui=/gui
|
||||
sebserver.webservice.ping.service.strategy=BLOCKING
|
||||
|
||||
|
||||
### Open API Documentation
|
||||
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.paths-to-exclude=/exam-api,/exam-api/discovery,/sebserver/error,/sebserver/check,/oauth,/exam-api/v1/*
|
||||
|
||||
|
||||
|
||||
### webservice API
|
||||
sebserver.webservice.api.admin.clientId=guiClient
|
||||
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.discovery=${sebserver.webservice.api.exam.endpoint}/discovery
|
||||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=43200
|
||||
sebserver.webservice.api.exam.event-handling-strategy=SINGLE_EVENT_STORE_STRATEGY
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=43200
|
||||
sebserver.webservice.api.exam.enable-indicator-cache=true
|
||||
sebserver.webservice.api.pagination.maxPageSize=500
|
||||
# comma separated list of known possible OpenEdX API access token request endpoints
|
||||
|
|
|
@ -66,6 +66,10 @@ sebserver.ssl.redirect.html.port=8080
|
|||
# features
|
||||
sebserver.feature.seb.screenProctoring=false
|
||||
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.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
|
||||
|
|
Loading…
Reference in a new issue