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
 | 
				
			||||||
| 
						 | 
					@ -63,3 +62,7 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -73,7 +77,6 @@ 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…
	
	Add table
		
		Reference in a new issue