SEBSERV-497 new impl and prepare for role based features
This commit is contained in:
parent
aec6bd6c04
commit
7ffef0938f
24 changed files with 325 additions and 235 deletions
|
@ -1,48 +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.gbl;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.CollectingStrategy;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
|
||||||
|
|
||||||
@Deprecated // we need another more flexible feature service that also take new User Role and Privileges into account
|
|
||||||
// SEBSERV-497
|
|
||||||
public interface FeatureService {
|
|
||||||
|
|
||||||
String FEATURE_SETTINGS_PREFIX = "sebserver.feature.";
|
|
||||||
|
|
||||||
enum ConfigurableFeature {
|
|
||||||
SCREEN_PROCTORING("seb.screenProctoring"),
|
|
||||||
INSTITUTION("admin.institution"),
|
|
||||||
REMOTE_PROCTORING("seb.remoteProctoring"),
|
|
||||||
TEST_LMS("lms.testLMS"),
|
|
||||||
EXAM_NO_LMS("exam.noLMS"),
|
|
||||||
LIGHT_SETUP("setup.light")
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
final String namespace;
|
|
||||||
|
|
||||||
ConfigurableFeature(final String namespace) {
|
|
||||||
this.namespace = namespace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isEnabled(ConfigurableFeature feature);
|
|
||||||
|
|
||||||
boolean isEnabled(LmsType LmsType);
|
|
||||||
|
|
||||||
boolean isEnabled(ProctoringServerType proctoringServerType);
|
|
||||||
|
|
||||||
boolean isEnabled(CollectingStrategy collectingRoomStrategy);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,72 +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.gbl;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.CollectingStrategy;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
|
||||||
|
|
||||||
@Lazy
|
|
||||||
@Service
|
|
||||||
@WebServiceProfile
|
|
||||||
@Deprecated // we need another more flexible feature service that also take new User Role and Privileges into account
|
|
||||||
// SEBSERV-497
|
|
||||||
public class FeatureServiceImpl implements FeatureService {
|
|
||||||
|
|
||||||
private final Environment environment;
|
|
||||||
|
|
||||||
public FeatureServiceImpl(final Environment environment) {
|
|
||||||
this.environment = environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(final LmsType LmsType) {
|
|
||||||
return this.environment.getProperty(toConfigName(
|
|
||||||
FEATURE_SETTINGS_PREFIX + LmsType.class.getSimpleName() + "."
|
|
||||||
+ LmsType.name()),
|
|
||||||
Boolean.class,
|
|
||||||
Boolean.TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(final CollectingStrategy collectingRoomStrategy) {
|
|
||||||
return this.environment.getProperty(toConfigName(
|
|
||||||
FEATURE_SETTINGS_PREFIX + CollectingStrategy.class.getSimpleName() + "."
|
|
||||||
+ collectingRoomStrategy.name()),
|
|
||||||
Boolean.class,
|
|
||||||
Boolean.TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(final ProctoringServerType proctoringServerType) {
|
|
||||||
return this.environment.getProperty(toConfigName(
|
|
||||||
FEATURE_SETTINGS_PREFIX + ProctoringServerType.class.getSimpleName() + "."
|
|
||||||
+ proctoringServerType.name()),
|
|
||||||
Boolean.class,
|
|
||||||
Boolean.TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(final ConfigurableFeature feature) {
|
|
||||||
return this.environment.getProperty(toConfigName(
|
|
||||||
FEATURE_SETTINGS_PREFIX + feature.namespace + ".enabled"),
|
|
||||||
Boolean.class,
|
|
||||||
Boolean.FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toConfigName(final String key) {
|
|
||||||
return key.replaceAll("_", "-");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -66,6 +66,8 @@ public final class API {
|
||||||
public static final String LOGIN_PATH_SEGMENT = "/loglogin";
|
public static final String LOGIN_PATH_SEGMENT = "/loglogin";
|
||||||
public static final String LOGOUT_PATH_SEGMENT = "/loglogout";
|
public static final String LOGOUT_PATH_SEGMENT = "/loglogout";
|
||||||
|
|
||||||
|
public static final String FEATURES_PATH_SEGMENT = "/features";
|
||||||
|
|
||||||
public static final String INFO_ENDPOINT = "/info";
|
public static final String INFO_ENDPOINT = "/info";
|
||||||
public static final String INFO_PARAM_INST_SUFFIX = "urlSuffix";
|
public static final String INFO_PARAM_INST_SUFFIX = "urlSuffix";
|
||||||
public static final String INFO_INST_PATH_SEGMENT = "/institution";
|
public static final String INFO_INST_PATH_SEGMENT = "/institution";
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package ch.ethz.seb.sebserver.gbl.model.user;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class UserFeatures {
|
||||||
|
|
||||||
|
public static final String ATTR_DEFAULT = "missingFeatureDefault";
|
||||||
|
public static final String ATTR_FEATURE_PRIVILEGES = "featurePrivileges";
|
||||||
|
|
||||||
|
public enum Feature {
|
||||||
|
INSTITUTION("admin.institution"),
|
||||||
|
SCREEN_PROCTORING("seb.screenProctoring"),
|
||||||
|
|
||||||
|
LIVE_PROCTORING("seb.liveProctoring"),
|
||||||
|
TEST_LMS("lms.type.MOCKUP"),
|
||||||
|
EXAM_NO_LMS("exam.noLMS"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
public final String featureName;
|
||||||
|
|
||||||
|
Feature(final String featureName) {
|
||||||
|
this.featureName = featureName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty(Domain.USER.ATTR_ID)
|
||||||
|
public final String userId;
|
||||||
|
@JsonProperty(ATTR_DEFAULT)
|
||||||
|
public final Boolean missingFeatureDefault;
|
||||||
|
@JsonProperty(ATTR_FEATURE_PRIVILEGES)
|
||||||
|
public final Map<String, Boolean> featurePrivileges;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public UserFeatures(
|
||||||
|
@JsonProperty(Domain.USER.ATTR_ID) final String userId,
|
||||||
|
@JsonProperty(ATTR_DEFAULT) final Boolean missingFeatureDefault,
|
||||||
|
@JsonProperty(ATTR_FEATURE_PRIVILEGES) final Map<String, Boolean> featurePrivileges) {
|
||||||
|
|
||||||
|
this.userId = userId;
|
||||||
|
this.missingFeatureDefault = missingFeatureDefault;
|
||||||
|
this.featurePrivileges = Utils.immutableMapOf(featurePrivileges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getMissingFeatureDefault() {
|
||||||
|
return missingFeatureDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Boolean> getFeaturePrivileges() {
|
||||||
|
return featurePrivileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFeatureEnabled(final Feature feature) {
|
||||||
|
return featurePrivileges.getOrDefault(feature.featureName, missingFeatureDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFeatureEnabled(final String featureName) {
|
||||||
|
return featurePrivileges.getOrDefault(featureName, missingFeatureDefault);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui;
|
package ch.ethz.seb.sebserver.gui;
|
||||||
|
|
||||||
import static ch.ethz.seb.sebserver.gbl.FeatureService.ConfigurableFeature.LIGHT_SETUP;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -35,10 +32,7 @@ public class GuiServiceInfo {
|
||||||
private final boolean distributedSetup;
|
private final boolean distributedSetup;
|
||||||
private final boolean multilingualGUI;
|
private final boolean multilingualGUI;
|
||||||
|
|
||||||
public final FeatureService featureService;
|
|
||||||
|
|
||||||
public GuiServiceInfo(
|
public GuiServiceInfo(
|
||||||
final FeatureService featureService,
|
|
||||||
@Value("${sebserver.version:--}") final String sebServerVersion,
|
@Value("${sebserver.version:--}") final String sebServerVersion,
|
||||||
@Value("${server.address}") final String internalServer,
|
@Value("${server.address}") final String internalServer,
|
||||||
@Value("${server.port}") final String internalPort,
|
@Value("${server.port}") final String internalPort,
|
||||||
|
@ -50,7 +44,6 @@ public class GuiServiceInfo {
|
||||||
@Value("${sebserver.webservice.distributed:false}") final boolean distributedSetup,
|
@Value("${sebserver.webservice.distributed:false}") final boolean distributedSetup,
|
||||||
@Value("${sebserver.gui.multilingual:false}") final boolean multilingualGUI) {
|
@Value("${sebserver.gui.multilingual:false}") final boolean multilingualGUI) {
|
||||||
|
|
||||||
this.featureService = featureService;
|
|
||||||
if (StringUtils.isBlank(externalScheme)) {
|
if (StringUtils.isBlank(externalScheme)) {
|
||||||
throw new RuntimeException("Missing mandatory inital parameter sebserver.gui.http.external.servername");
|
throw new RuntimeException("Missing mandatory inital parameter sebserver.gui.http.external.servername");
|
||||||
}
|
}
|
||||||
|
@ -88,10 +81,6 @@ public class GuiServiceInfo {
|
||||||
this.multilingualGUI = multilingualGUI;
|
this.multilingualGUI = multilingualGUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLightSetup() {
|
|
||||||
return this.featureService.isEnabled(LIGHT_SETUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExternalScheme() {
|
public String getExternalScheme() {
|
||||||
return this.externalScheme;
|
return this.externalScheme;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,11 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.content.exam;
|
package ch.ethz.seb.sebserver.gui.content.exam;
|
||||||
|
|
||||||
import static ch.ethz.seb.sebserver.gbl.FeatureService.ConfigurableFeature.SCREEN_PROCTORING;
|
import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.SCREEN_PROCTORING;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.*;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.*;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -124,7 +123,6 @@ public class ExamForm implements TemplateComposer {
|
||||||
private final ExamIndicatorsList examIndicatorsList;
|
private final ExamIndicatorsList examIndicatorsList;
|
||||||
private final ExamClientGroupList examClientGroupList;
|
private final ExamClientGroupList examClientGroupList;
|
||||||
private final ExamCreateClientConfigPopup examCreateClientConfigPopup;
|
private final ExamCreateClientConfigPopup examCreateClientConfigPopup;
|
||||||
private final FeatureService featureService;
|
|
||||||
|
|
||||||
protected ExamForm(
|
protected ExamForm(
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
|
@ -137,8 +135,7 @@ public class ExamForm implements TemplateComposer {
|
||||||
final ExamFormConfigs examFormConfigs,
|
final ExamFormConfigs examFormConfigs,
|
||||||
final ExamIndicatorsList examIndicatorsList,
|
final ExamIndicatorsList examIndicatorsList,
|
||||||
final ExamClientGroupList examClientGroupList,
|
final ExamClientGroupList examClientGroupList,
|
||||||
final ExamCreateClientConfigPopup examCreateClientConfigPopup,
|
final ExamCreateClientConfigPopup examCreateClientConfigPopup) {
|
||||||
final FeatureService featureService) {
|
|
||||||
|
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.resourceService = pageService.getResourceService();
|
this.resourceService = pageService.getResourceService();
|
||||||
|
@ -152,7 +149,6 @@ public class ExamForm implements TemplateComposer {
|
||||||
this.examIndicatorsList = examIndicatorsList;
|
this.examIndicatorsList = examIndicatorsList;
|
||||||
this.examClientGroupList = examClientGroupList;
|
this.examClientGroupList = examClientGroupList;
|
||||||
this.examCreateClientConfigPopup = examCreateClientConfigPopup;
|
this.examCreateClientConfigPopup = examCreateClientConfigPopup;
|
||||||
this.featureService = featureService;
|
|
||||||
|
|
||||||
this.consistencyMessageMapping = new HashMap<>();
|
this.consistencyMessageMapping = new HashMap<>();
|
||||||
this.consistencyMessageMapping.put(
|
this.consistencyMessageMapping.put(
|
||||||
|
@ -255,7 +251,7 @@ public class ExamForm implements TemplateComposer {
|
||||||
.map(ProctoringServiceSettings::getEnableProctoring)
|
.map(ProctoringServiceSettings::getEnableProctoring)
|
||||||
.getOr(false);
|
.getOr(false);
|
||||||
|
|
||||||
final boolean spsFeatureEnabled = this.featureService.isEnabled(SCREEN_PROCTORING);
|
final boolean spsFeatureEnabled = currentUser.isFeatureEnabled(SCREEN_PROCTORING);
|
||||||
final boolean screenProctoringEnabled = readonly && spsFeatureEnabled && this.restService
|
final boolean screenProctoringEnabled = readonly && spsFeatureEnabled && this.restService
|
||||||
.getBuilder(GetScreenProctoringSettings.class)
|
.getBuilder(GetScreenProctoringSettings.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||||
|
|
|
@ -8,14 +8,13 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.content.exam;
|
package ch.ethz.seb.sebserver.gui.content.exam;
|
||||||
|
|
||||||
import static ch.ethz.seb.sebserver.gbl.FeatureService.ConfigurableFeature.EXAM_NO_LMS;
|
import static ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature.EXAM_NO_LMS;
|
||||||
import static ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys.NEW_EXAM_NO_LMS;
|
import static ch.ethz.seb.sebserver.gui.service.page.PageContext.AttributeKeys.NEW_EXAM_NO_LMS;
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.eclipse.rap.rwt.RWT;
|
import org.eclipse.rap.rwt.RWT;
|
||||||
import org.eclipse.swt.widgets.Composite;
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
@ -149,7 +148,6 @@ public class ExamList implements TemplateComposer {
|
||||||
final CurrentUser currentUser = this.resourceService.getCurrentUser();
|
final CurrentUser currentUser = this.resourceService.getCurrentUser();
|
||||||
final RestService restService = this.resourceService.getRestService();
|
final RestService restService = this.resourceService.getRestService();
|
||||||
final I18nSupport i18nSupport = this.resourceService.getI18nSupport();
|
final I18nSupport i18nSupport = this.resourceService.getI18nSupport();
|
||||||
final FeatureService featureService = this.pageService.getFeatureService();
|
|
||||||
|
|
||||||
// content page layout with title
|
// content page layout with title
|
||||||
final Composite content = widgetFactory.defaultPageLayout(
|
final Composite content = widgetFactory.defaultPageLayout(
|
||||||
|
@ -286,7 +284,7 @@ public class ExamList implements TemplateComposer {
|
||||||
|
|
||||||
.newAction(ActionDefinition.EXAM_NEW)
|
.newAction(ActionDefinition.EXAM_NEW)
|
||||||
.withAttribute(NEW_EXAM_NO_LMS, Constants.TRUE_STRING)
|
.withAttribute(NEW_EXAM_NO_LMS, Constants.TRUE_STRING)
|
||||||
.publishIf(() -> userGrant.iw() && featureService.isEnabled(EXAM_NO_LMS))
|
.publishIf(() -> userGrant.iw() && currentUser.isFeatureEnabled(EXAM_NO_LMS))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.gui.content.monitoring;
|
package ch.ethz.seb.sebserver.gui.content.monitoring;
|
||||||
|
|
||||||
import static ch.ethz.seb.sebserver.gbl.FeatureService.ConfigurableFeature.SCREEN_PROCTORING;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -20,7 +18,6 @@ import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.text.StringEscapeUtils;
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
|
@ -112,7 +109,6 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
private final MonitoringExamSearchPopup monitoringExamSearchPopup;
|
private final MonitoringExamSearchPopup monitoringExamSearchPopup;
|
||||||
private final SEBSendLockPopup sebSendLockPopup;
|
private final SEBSendLockPopup sebSendLockPopup;
|
||||||
private final MonitoringProctoringService monitoringProctoringService;
|
private final MonitoringProctoringService monitoringProctoringService;
|
||||||
private final FeatureService featureService;
|
|
||||||
private final boolean distributedSetup;
|
private final boolean distributedSetup;
|
||||||
private final long pollInterval;
|
private final long pollInterval;
|
||||||
|
|
||||||
|
@ -125,7 +121,6 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
final SEBSendLockPopup sebSendLockPopup,
|
final SEBSendLockPopup sebSendLockPopup,
|
||||||
final MonitoringProctoringService monitoringProctoringService,
|
final MonitoringProctoringService monitoringProctoringService,
|
||||||
final GuiServiceInfo guiServiceInfo,
|
final GuiServiceInfo guiServiceInfo,
|
||||||
final FeatureService featureService,
|
|
||||||
@Value("${sebserver.gui.webservice.poll-interval:2000}") final long pollInterval) {
|
@Value("${sebserver.gui.webservice.poll-interval:2000}") final long pollInterval) {
|
||||||
|
|
||||||
this.serverPushService = serverPushService;
|
this.serverPushService = serverPushService;
|
||||||
|
@ -139,7 +134,6 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
this.distributedSetup = guiServiceInfo.isDistributedSetup();
|
this.distributedSetup = guiServiceInfo.isDistributedSetup();
|
||||||
this.monitoringExamSearchPopup = monitoringExamSearchPopup;
|
this.monitoringExamSearchPopup = monitoringExamSearchPopup;
|
||||||
this.sebSendLockPopup = sebSendLockPopup;
|
this.sebSendLockPopup = sebSendLockPopup;
|
||||||
this.featureService = featureService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -321,9 +315,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
final PageActionBuilder actionBuilder = this.pageService
|
final PageActionBuilder actionBuilder = this.pageService
|
||||||
.pageActionBuilder(pageContext.clearEntityKeys());
|
.pageActionBuilder(pageContext.clearEntityKeys());
|
||||||
|
|
||||||
final boolean spsFeatureEnabled = this.featureService.isEnabled(SCREEN_PROCTORING);
|
final boolean proctoringEnabled = proctoringSettings != null &&
|
||||||
final boolean proctoringEnabled = spsFeatureEnabled &&
|
|
||||||
proctoringSettings != null &&
|
|
||||||
BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
|
BooleanUtils.toBoolean(proctoringSettings.enableProctoring);
|
||||||
final boolean screenProctoringEnabled = screenProctoringSettings != null &&
|
final boolean screenProctoringEnabled = screenProctoringSettings != null &&
|
||||||
BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring);
|
BooleanUtils.toBoolean(screenProctoringSettings.enableScreenProctoring);
|
||||||
|
|
|
@ -92,7 +92,8 @@ import ch.ethz.seb.sebserver.gui.service.session.MonitoringEntry;
|
||||||
@Service
|
@Service
|
||||||
@GuiProfile
|
@GuiProfile
|
||||||
/** Defines functionality to get resources or functions of resources to feed e.g. selection or
|
/** Defines functionality to get resources or functions of resources to feed e.g. selection or
|
||||||
* combo-box content. */
|
* combo-box content.
|
||||||
|
* */
|
||||||
public class ResourceService {
|
public class ResourceService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ResourceService.class);
|
private static final Logger log = LoggerFactory.getLogger(ResourceService.class);
|
||||||
|
@ -139,8 +140,6 @@ public class ResourceService {
|
||||||
public static final String EXAM_PROCTORING_FEATURES_PREFIX = "sebserver.exam.proctoring.form.features.";
|
public static final String EXAM_PROCTORING_FEATURES_PREFIX = "sebserver.exam.proctoring.form.features.";
|
||||||
public static final String VDI_TYPE_PREFIX = "sebserver.clientconfig.form.vditype.";
|
public static final String VDI_TYPE_PREFIX = "sebserver.clientconfig.form.vditype.";
|
||||||
|
|
||||||
private static final String DISABLE_LMS_FLAG = "sebserver.gui.webservice.lms.disable.";
|
|
||||||
|
|
||||||
public static final EnumSet<AttributeType> ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of(
|
public static final EnumSet<AttributeType> ATTRIBUTE_TYPES_NOT_DISPLAYED = EnumSet.of(
|
||||||
AttributeType.LABEL,
|
AttributeType.LABEL,
|
||||||
AttributeType.COMPOSITE_TABLE,
|
AttributeType.COMPOSITE_TABLE,
|
||||||
|
@ -159,7 +158,6 @@ public class ResourceService {
|
||||||
private final I18nSupport i18nSupport;
|
private final I18nSupport i18nSupport;
|
||||||
private final RestService restService;
|
private final RestService restService;
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
private final EnumSet<LmsType> disabledLmsTypes;
|
|
||||||
|
|
||||||
protected ResourceService(
|
protected ResourceService(
|
||||||
final I18nSupport i18nSupport,
|
final I18nSupport i18nSupport,
|
||||||
|
@ -170,13 +168,6 @@ public class ResourceService {
|
||||||
this.i18nSupport = i18nSupport;
|
this.i18nSupport = i18nSupport;
|
||||||
this.restService = restService;
|
this.restService = restService;
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
|
|
||||||
this.disabledLmsTypes = EnumSet.noneOf(LmsType.class);
|
|
||||||
final List<LmsType> disabled = Arrays.asList(LmsType.values()).stream()
|
|
||||||
.filter(lmsType -> lmsType.features.isEmpty() ||
|
|
||||||
environment.getProperty(DISABLE_LMS_FLAG + lmsType.name(), Boolean.class, false) == true)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
this.disabledLmsTypes.addAll(disabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public I18nSupport getI18nSupport() {
|
public I18nSupport getI18nSupport() {
|
||||||
|
@ -206,7 +197,7 @@ public class ResourceService {
|
||||||
|
|
||||||
public List<Tuple<String>> lmsTypeResources() {
|
public List<Tuple<String>> lmsTypeResources() {
|
||||||
return Arrays.stream(LmsType.values())
|
return Arrays.stream(LmsType.values())
|
||||||
.filter(lmsType -> !this.disabledLmsTypes.contains(lmsType))
|
.filter(lmsType -> this.currentUser.isFeatureEnabled("lms.type." + lmsType.name()))
|
||||||
.map(lmsType -> new Tuple<>(
|
.map(lmsType -> new Tuple<>(
|
||||||
lmsType.name(),
|
lmsType.name(),
|
||||||
this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name())))
|
this.i18nSupport.getText(LMSSETUP_TYPE_PREFIX + lmsType.name(), lmsType.name())))
|
||||||
|
|
|
@ -17,7 +17,6 @@ import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
import org.eclipse.swt.custom.ScrolledComposite;
|
import org.eclipse.swt.custom.ScrolledComposite;
|
||||||
|
@ -76,8 +75,6 @@ public interface PageService {
|
||||||
|
|
||||||
Cryptor getCryptor();
|
Cryptor getCryptor();
|
||||||
|
|
||||||
FeatureService getFeatureService();
|
|
||||||
|
|
||||||
/** Get the WidgetFactory service
|
/** Get the WidgetFactory service
|
||||||
*
|
*
|
||||||
* @return the WidgetFactory service */
|
* @return the WidgetFactory service */
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
|
||||||
import org.eclipse.rap.rwt.RWT;
|
import org.eclipse.rap.rwt.RWT;
|
||||||
import org.eclipse.swt.widgets.TreeItem;
|
import org.eclipse.swt.widgets.TreeItem;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -89,7 +88,6 @@ public class PageServiceImpl implements PageService {
|
||||||
private final ResourceService resourceService;
|
private final ResourceService resourceService;
|
||||||
private final CurrentUser currentUser;
|
private final CurrentUser currentUser;
|
||||||
private final ServerPushService serverPushService;
|
private final ServerPushService serverPushService;
|
||||||
private final FeatureService featureService;
|
|
||||||
|
|
||||||
public PageServiceImpl(
|
public PageServiceImpl(
|
||||||
final Cryptor cryptor,
|
final Cryptor cryptor,
|
||||||
|
@ -98,8 +96,7 @@ public class PageServiceImpl implements PageService {
|
||||||
final PolyglotPageService polyglotPageService,
|
final PolyglotPageService polyglotPageService,
|
||||||
final ResourceService resourceService,
|
final ResourceService resourceService,
|
||||||
final CurrentUser currentUser,
|
final CurrentUser currentUser,
|
||||||
final ServerPushService serverPushService,
|
final ServerPushService serverPushService) {
|
||||||
final FeatureService featureService) {
|
|
||||||
|
|
||||||
this.cryptor = cryptor;
|
this.cryptor = cryptor;
|
||||||
this.jsonMapper = jsonMapper;
|
this.jsonMapper = jsonMapper;
|
||||||
|
@ -108,7 +105,6 @@ public class PageServiceImpl implements PageService {
|
||||||
this.resourceService = resourceService;
|
this.resourceService = resourceService;
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
this.serverPushService = serverPushService;
|
this.serverPushService = serverPushService;
|
||||||
this.featureService = featureService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,11 +112,6 @@ public class PageServiceImpl implements PageService {
|
||||||
return this.cryptor;
|
return this.cryptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeatureService getFeatureService() {
|
|
||||||
return this.featureService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WidgetFactory getWidgetFactory() {
|
public WidgetFactory getWidgetFactory() {
|
||||||
return this.widgetFactory;
|
return this.widgetFactory;
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package ch.ethz.seb.sebserver.gui.service.remote.webservice.api.useraccount;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetUserFeatures extends RestCall<UserFeatures> {
|
||||||
|
|
||||||
|
public GetUserFeatures() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_SINGLE,
|
||||||
|
EntityType.USER,
|
||||||
|
new TypeReference<UserFeatures>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.USER_ACCOUNT_ENDPOINT + API.CURRENT_USER_PATH_SEGMENT + API.FEATURES_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
|
@ -47,6 +48,8 @@ public class CurrentUser {
|
||||||
private final AuthorizationContextHolder authorizationContextHolder;
|
private final AuthorizationContextHolder authorizationContextHolder;
|
||||||
private SEBServerAuthorizationContext authContext = null;
|
private SEBServerAuthorizationContext authContext = null;
|
||||||
private Map<RoleTypeKey, Privilege> privileges = null;
|
private Map<RoleTypeKey, Privilege> privileges = null;
|
||||||
|
|
||||||
|
private UserFeatures features = null;
|
||||||
private final Map<String, String> attributes;
|
private final Map<String, String> attributes;
|
||||||
private final ProctoringGUIService proctoringGUIService;
|
private final ProctoringGUIService proctoringGUIService;
|
||||||
|
|
||||||
|
@ -192,6 +195,7 @@ public class CurrentUser {
|
||||||
|
|
||||||
this.proctoringGUIService.clear();
|
this.proctoringGUIService.clear();
|
||||||
this.privileges = null;
|
this.privileges = null;
|
||||||
|
this.features = null;
|
||||||
|
|
||||||
if (isAvailable()) {
|
if (isAvailable()) {
|
||||||
if (this.authContext.logout()) {
|
if (this.authContext.logout()) {
|
||||||
|
@ -272,6 +276,42 @@ public class CurrentUser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFeatureEnabled(final UserFeatures.Feature feature) {
|
||||||
|
loadFeatures();
|
||||||
|
return this.features != null && this.features.isFeatureEnabled(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFeatureEnabled(final String featureName) {
|
||||||
|
loadFeatures();
|
||||||
|
return this.features != null && this.features.isFeatureEnabled(featureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFeatures() {
|
||||||
|
if (this.features != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContext();
|
||||||
|
if (this.authContext != null) {
|
||||||
|
try {
|
||||||
|
final WebserviceURIService webserviceURIService =
|
||||||
|
this.authorizationContextHolder.getWebserviceURIService();
|
||||||
|
this.features = this.authContext.getRestTemplate()
|
||||||
|
.exchange(
|
||||||
|
webserviceURIService.getURIBuilder()
|
||||||
|
.path(API.USER_ACCOUNT_ENDPOINT + API.CURRENT_USER_PATH_SEGMENT + API.FEATURES_PATH_SEGMENT)
|
||||||
|
.toUriString(),
|
||||||
|
HttpMethod.GET,
|
||||||
|
HttpEntity.EMPTY,
|
||||||
|
UserFeatures.class)
|
||||||
|
.getBody();
|
||||||
|
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Failed to load user feature privileges: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Wrapper can be used for base and institutional grant checks for a specified EntityType */
|
/** Wrapper can be used for base and institutional grant checks for a specified EntityType */
|
||||||
public class GrantCheck {
|
public class GrantCheck {
|
||||||
private final EntityType entityType;
|
private final EntityType entityType;
|
||||||
|
|
|
@ -8,20 +8,14 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice;
|
package ch.ethz.seb.sebserver.webservice;
|
||||||
|
|
||||||
import static ch.ethz.seb.sebserver.gbl.FeatureService.ConfigurableFeature.LIGHT_SETUP;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
import java.util.stream.Collectors;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.FeatureService;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
|
import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.FeatureService;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -66,6 +60,7 @@ public class WebserviceInfo {
|
||||||
private final String discoveryEndpoint;
|
private final String discoveryEndpoint;
|
||||||
private final String contextPath;
|
private final String contextPath;
|
||||||
|
|
||||||
|
private final boolean isLightSetup;
|
||||||
private final String serverURLPrefix;
|
private final String serverURLPrefix;
|
||||||
private final boolean isDistributed;
|
private final boolean isDistributed;
|
||||||
private final String webserviceUUID;
|
private final String webserviceUUID;
|
||||||
|
@ -76,6 +71,7 @@ public class WebserviceInfo {
|
||||||
private final Set<String> activeProfiles;
|
private final Set<String> activeProfiles;
|
||||||
|
|
||||||
private final WebserviceInfoDAO webserviceInfoDAO;
|
private final WebserviceInfoDAO webserviceInfoDAO;
|
||||||
|
private final FeatureService featureService;
|
||||||
private boolean isMaster = false;
|
private boolean isMaster = false;
|
||||||
|
|
||||||
@Value("${sebserver.webservice.api.admin.accessTokenValiditySeconds:3600}")
|
@Value("${sebserver.webservice.api.admin.accessTokenValiditySeconds:3600}")
|
||||||
|
@ -85,18 +81,16 @@ public class WebserviceInfo {
|
||||||
@Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}")
|
@Value("${sebserver.webservice.api.exam.accessTokenValiditySeconds:43200}")
|
||||||
private int examAPITokenValiditySeconds;
|
private int examAPITokenValiditySeconds;
|
||||||
|
|
||||||
public final FeatureService featureService;
|
|
||||||
|
|
||||||
private final ScreenProctoringServiceBundle screenProctoringServiceBundle;
|
private final ScreenProctoringServiceBundle screenProctoringServiceBundle;
|
||||||
|
|
||||||
public WebserviceInfo(
|
public WebserviceInfo(
|
||||||
final WebserviceInfoDAO webserviceInfoDAO,
|
final WebserviceInfoDAO webserviceInfoDAO,
|
||||||
|
final FeatureService featureService,
|
||||||
final Environment environment,
|
final Environment environment,
|
||||||
final Cryptor cryptor,
|
final Cryptor cryptor) {
|
||||||
final FeatureService featureService) {
|
|
||||||
|
|
||||||
this.featureService = featureService;
|
|
||||||
this.webserviceInfoDAO = webserviceInfoDAO;
|
this.webserviceInfoDAO = webserviceInfoDAO;
|
||||||
|
this.featureService = featureService;
|
||||||
this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY);
|
this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY);
|
||||||
this.testProperty = environment.getProperty(WEB_SERVICE_TEST_PROPERTY, "NOT_AVAILABLE");
|
this.testProperty = environment.getProperty(WEB_SERVICE_TEST_PROPERTY, "NOT_AVAILABLE");
|
||||||
this.httpScheme = environment.getRequiredProperty(WEB_SERVICE_HTTP_SCHEME_KEY);
|
this.httpScheme = environment.getRequiredProperty(WEB_SERVICE_HTTP_SCHEME_KEY);
|
||||||
|
@ -135,6 +129,9 @@ public class WebserviceInfo {
|
||||||
}
|
}
|
||||||
this.serverURLPrefix = builder.toUriString();
|
this.serverURLPrefix = builder.toUriString();
|
||||||
|
|
||||||
|
this.isLightSetup = BooleanUtils.toBoolean(environment.getProperty(
|
||||||
|
"sebserver.webservice.light.setup",
|
||||||
|
Constants.FALSE_STRING));
|
||||||
this.isDistributed = BooleanUtils.toBoolean(environment.getProperty(
|
this.isDistributed = BooleanUtils.toBoolean(environment.getProperty(
|
||||||
"sebserver.webservice.distributed",
|
"sebserver.webservice.distributed",
|
||||||
Constants.FALSE_STRING));
|
Constants.FALSE_STRING));
|
||||||
|
@ -180,8 +177,11 @@ public class WebserviceInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLightSetup() {
|
public Map<String, Boolean> configuredFeatures() {
|
||||||
return this.featureService.isEnabled(LIGHT_SETUP);
|
return Arrays.stream(UserFeatures.Feature.values()).collect(Collectors.toMap(
|
||||||
|
f -> f.featureName,
|
||||||
|
featureService::isEnabledByConfig
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMaster() {
|
public boolean isMaster() {
|
||||||
|
@ -276,7 +276,7 @@ public class WebserviceInfo {
|
||||||
|
|
||||||
/** Get the server URL prefix in the form of;
|
/** Get the server URL prefix in the form of;
|
||||||
* [scheme{http|https}]://[server-address{DNS-name|IP}]:[port]
|
* [scheme{http|https}]://[server-address{DNS-name|IP}]:[port]
|
||||||
*
|
* <p>
|
||||||
* E.g.: https://seb.server.ch:8080
|
* E.g.: https://seb.server.ch:8080
|
||||||
*
|
*
|
||||||
* @return the server URL prefix */
|
* @return the server URL prefix */
|
||||||
|
@ -284,6 +284,9 @@ public class WebserviceInfo {
|
||||||
return this.serverURLPrefix;
|
return this.serverURLPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLightSetup() {
|
||||||
|
return this.isLightSetup;
|
||||||
|
}
|
||||||
public boolean isDistributed() {
|
public boolean isDistributed() {
|
||||||
return this.isDistributed;
|
return this.isDistributed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,10 +121,11 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
|
||||||
this.environment.getProperty("sebserver.webservice.distributed.connectionUpdate", "2000"));
|
this.environment.getProperty("sebserver.webservice.distributed.connectionUpdate", "2000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.webserviceInfo.isLightSetup()) {
|
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
SEBServerInit.INIT_LOGGER.info("----> Configured Features:");
|
||||||
SEBServerInit.INIT_LOGGER.info("----> SEB Server light setup enabled");
|
this.webserviceInfo.configuredFeatures().entrySet().stream().forEach(entry -> {
|
||||||
}
|
SEBServerInit.INIT_LOGGER.info("----> {} --> {}", entry.getKey(), entry.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
SEBServerInit.INIT_LOGGER.info("----> ");
|
SEBServerInit.INIT_LOGGER.info("----> ");
|
||||||
SEBServerInit.INIT_LOGGER.info("----> Working with ping service: {}",
|
SEBServerInit.INIT_LOGGER.info("----> Working with ping service: {}",
|
||||||
|
|
|
@ -18,13 +18,14 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
|
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||||
|
|
||||||
/** A service to check authorization grants for a given user for entity-types and -instances
|
/** A service to check authorization grants for a given user for entity-types and -instances
|
||||||
*
|
* <p>
|
||||||
* If there is one or more GrantEntity objects within an authenticated user-request, this service
|
* If there is one or more GrantEntity objects within an authenticated user-request, this service
|
||||||
* can be used check the authenticated user access grant within the object. Check if a given user
|
* can be used check the authenticated user access grant within the object. Check if a given user
|
||||||
* has write, modify or even read rights on an entity instance or on an entity type. */
|
* has write, modify or even read rights on an entity instance or on an entity type. */
|
||||||
|
@ -40,17 +41,19 @@ public interface AuthorizationService {
|
||||||
* @return all registered Privileges */
|
* @return all registered Privileges */
|
||||||
Collection<Privilege> getAllPrivileges();
|
Collection<Privilege> getAllPrivileges();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Check grant on privilege type for specified EntityType and for the given user and institution.
|
/** Check grant on privilege type for specified EntityType and for the given user and institution.
|
||||||
*
|
* <p>
|
||||||
* This makes a privilege grant check for every UserRole given. The first found successful grant
|
* This makes a privilege grant check for every UserRole given. The first found successful grant
|
||||||
* will immediately return true
|
* will immediately return true
|
||||||
*
|
* <p>
|
||||||
* The privilege grant check function always checks first the base privilege with no institutional or owner grant.
|
* The privilege grant check function always checks first the base privilege with no institutional or owner grant.
|
||||||
* If user has a grant on base privileges this returns true without checking further institutional or owner grant
|
* If user has a grant on base privileges this returns true without checking further institutional or owner grant
|
||||||
* If user has no base privilege grant the function checks further grants, first the institutional grant, where
|
* If user has no base privilege grant the function checks further grants, first the institutional grant, where
|
||||||
* the institution id and the users institution id must match and further more the owner grant, where ownerId
|
* the institution id and the users institution id must match and further more the owner grant, where ownerId
|
||||||
* and the users id must match.
|
* and the users id must match.
|
||||||
*
|
* <p>
|
||||||
* see Privilege.hasGrant for more information how the overall grant function works
|
* see Privilege.hasGrant for more information how the overall grant function works
|
||||||
*
|
*
|
||||||
* @param privilegeType The privilege type to check
|
* @param privilegeType The privilege type to check
|
||||||
|
@ -71,7 +74,7 @@ public interface AuthorizationService {
|
||||||
Set<UserRole> userRoles);
|
Set<UserRole> userRoles);
|
||||||
|
|
||||||
/** Check grant for a given privilege type and entity type for the current user.
|
/** Check grant for a given privilege type and entity type for the current user.
|
||||||
*
|
* <p>
|
||||||
* NOTE: This only checks the base privilege grant because there is no Entity specific information
|
* NOTE: This only checks the base privilege grant because there is no Entity specific information
|
||||||
*
|
*
|
||||||
* @param privilegeType the privilege type to check
|
* @param privilegeType the privilege type to check
|
||||||
|
@ -109,11 +112,11 @@ public interface AuthorizationService {
|
||||||
|
|
||||||
/** Check base privilege grant and institutional privilege grant for a given privilege type
|
/** Check base privilege grant and institutional privilege grant for a given privilege type
|
||||||
* on a given entity type.
|
* on a given entity type.
|
||||||
*
|
* <p>
|
||||||
* If the question is similar like this:
|
* If the question is similar like this:
|
||||||
* "Has the current user that belongs to institution A the right to create an entity of
|
* "Has the current user that belongs to institution A the right to create an entity of
|
||||||
* type X on institution B", then this is the answer, use:
|
* type X on institution B", then this is the answer, use:
|
||||||
*
|
* <p>
|
||||||
* hasPrivilege(PrivilegeType.WRITE, EntityType.X, B)
|
* hasPrivilege(PrivilegeType.WRITE, EntityType.X, B)
|
||||||
*
|
*
|
||||||
* @param privilegeType the privilege type to check
|
* @param privilegeType the privilege type to check
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
|
||||||
|
public interface FeatureService {
|
||||||
|
|
||||||
|
public static final String FEATURE_CONFIG_PREFIX = "sebserver.feature.";
|
||||||
|
|
||||||
|
/** Get all feature flags for current user.
|
||||||
|
*
|
||||||
|
* @return UserFeatures all feature flags for current user */
|
||||||
|
Result<UserFeatures> getCurrentUserFeatures();
|
||||||
|
|
||||||
|
Map<UserFeatures.Feature, Boolean> getUserRoleDefaults(UserRole role);
|
||||||
|
|
||||||
|
boolean isEnabledByConfig(final UserFeatures.Feature feature);
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.authorization;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
||||||
|
@ -29,10 +30,10 @@ public interface UserService {
|
||||||
SEBServerUser getCurrentUser();
|
SEBServerUser getCurrentUser();
|
||||||
|
|
||||||
/** Extracts the internal SEBServerUser from a given Principal.
|
/** Extracts the internal SEBServerUser from a given Principal.
|
||||||
*
|
* <p>
|
||||||
* This is attended to apply some known strategies to extract the internal user from Principal. If there is no
|
* This is attended to apply some known strategies to extract the internal user from Principal. If there is no
|
||||||
* internal user found on the given Principal, a IllegalArgumentException is thrown.
|
* internal user found on the given Principal, a IllegalArgumentException is thrown.
|
||||||
*
|
* <p>
|
||||||
* If there is certainly a internal user within the given Principal but no strategy that finds it, this method can
|
* If there is certainly a internal user within the given Principal but no strategy that finds it, this method can
|
||||||
* be extended with the needed strategy.
|
* be extended with the needed strategy.
|
||||||
*
|
*
|
||||||
|
@ -54,7 +55,7 @@ public interface UserService {
|
||||||
|
|
||||||
/** Used to set authentication on different thread.
|
/** Used to set authentication on different thread.
|
||||||
*
|
*
|
||||||
* @param authentication */
|
* @param authentication the Authentication context*/
|
||||||
void setAuthenticationIfAbsent(Authentication authentication);
|
void setAuthenticationIfAbsent(Authentication authentication);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures.Feature;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.FeatureService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Service
|
||||||
|
@WebServiceProfile
|
||||||
|
public class FeatureServiceImpl implements FeatureService {
|
||||||
|
|
||||||
|
private final Environment environment;
|
||||||
|
private final UserService userService;
|
||||||
|
private final UserDAO userDAO;
|
||||||
|
|
||||||
|
public FeatureServiceImpl(
|
||||||
|
final Environment environment,
|
||||||
|
final UserService userService,
|
||||||
|
final UserDAO userDAO) {
|
||||||
|
|
||||||
|
this.environment = environment;
|
||||||
|
this.userService = userService;
|
||||||
|
this.userDAO = userDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<UserFeatures> getCurrentUserFeatures() {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
final String userId = userService.getCurrentUser().getUserInfo().uuid;
|
||||||
|
final EnumSet<Feature> userEnabledFeatures = getUserEnabledFeatures(userId);
|
||||||
|
final Map<String, Boolean> features = Arrays.stream(Feature.values()).collect(Collectors.toMap(
|
||||||
|
f -> f.featureName,
|
||||||
|
f -> isEnabledByConfig(f) && userEnabledFeatures.contains(f)
|
||||||
|
));
|
||||||
|
|
||||||
|
return new UserFeatures(userId, true, features);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Feature, Boolean> getUserRoleDefaults(final UserRole role) {
|
||||||
|
// TODO implement this when user role based features are available
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumSet<Feature> getUserEnabledFeatures(final String userId) {
|
||||||
|
// TODO implement this when user role based features are available
|
||||||
|
return EnumSet.allOf(Feature.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabledByConfig(final Feature feature) {
|
||||||
|
final String configName = getConfigName(feature);
|
||||||
|
try {
|
||||||
|
return this.environment.getProperty(configName, Boolean.class, false)
|
||||||
|
|| this.environment.getProperty(configName + ".enabled", Boolean.class);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// NOTE: for now if there is not explicitly disabled from config, the feature is considered enabled
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConfigName(final Feature feature) {
|
||||||
|
return getConfigName(feature.featureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConfigName(final String featureName) {
|
||||||
|
return FEATURE_CONFIG_PREFIX + featureName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,9 @@ import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.user.*;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Pair;
|
import ch.ethz.seb.sebserver.gbl.util.Pair;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.FeatureService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
|
||||||
import org.mybatis.dynamic.sql.SqlTable;
|
import org.mybatis.dynamic.sql.SqlTable;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
@ -38,12 +40,6 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
|
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.PasswordChange;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserLogActivityType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserMod;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
|
||||||
|
@ -66,6 +62,8 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
||||||
private final PasswordEncoder userPasswordEncoder;
|
private final PasswordEncoder userPasswordEncoder;
|
||||||
private final ScreenProctoringService screenProctoringService;
|
private final ScreenProctoringService screenProctoringService;
|
||||||
|
|
||||||
|
private final FeatureService featureService;
|
||||||
|
|
||||||
public UserAccountController(
|
public UserAccountController(
|
||||||
final UserDAO userDAO,
|
final UserDAO userDAO,
|
||||||
final AuthorizationService authorization,
|
final AuthorizationService authorization,
|
||||||
|
@ -75,6 +73,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
||||||
final ApplicationEventPublisher applicationEventPublisher,
|
final ApplicationEventPublisher applicationEventPublisher,
|
||||||
final BeanValidationService beanValidationService,
|
final BeanValidationService beanValidationService,
|
||||||
final ScreenProctoringService screenProctoringService,
|
final ScreenProctoringService screenProctoringService,
|
||||||
|
final FeatureService featureService,
|
||||||
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
|
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
|
||||||
|
|
||||||
super(authorization,
|
super(authorization,
|
||||||
|
@ -87,6 +86,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
||||||
this.userDAO = userDAO;
|
this.userDAO = userDAO;
|
||||||
this.userPasswordEncoder = userPasswordEncoder;
|
this.userPasswordEncoder = userPasswordEncoder;
|
||||||
this.screenProctoringService = screenProctoringService;
|
this.screenProctoringService = screenProctoringService;
|
||||||
|
this.featureService = featureService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(path = API.CURRENT_USER_PATH_SEGMENT, method = RequestMethod.GET)
|
@RequestMapping(path = API.CURRENT_USER_PATH_SEGMENT, method = RequestMethod.GET)
|
||||||
|
@ -97,6 +97,14 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
||||||
.getUserInfo();
|
.getUserInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.CURRENT_USER_PATH_SEGMENT + API.FEATURES_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public UserFeatures getCurrentUserFeatures() {
|
||||||
|
return this.featureService.getCurrentUserFeatures().getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(path = API.LOGIN_PATH_SEGMENT, method = RequestMethod.POST)
|
@RequestMapping(path = API.LOGIN_PATH_SEGMENT, method = RequestMethod.POST)
|
||||||
public void logLogin() {
|
public void logLogin() {
|
||||||
this.userActivityLogDAO.logLogin(this.authorization
|
this.userActivityLogDAO.logLogin(this.authorization
|
||||||
|
|
|
@ -68,7 +68,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/*
|
||||||
|
|
||||||
|
# features
|
||||||
|
sebserver.feature.seb.screenProctoring.enabled=true
|
||||||
sebserver.feature.seb.screenProctoring.bundled=true
|
sebserver.feature.seb.screenProctoring.bundled=true
|
||||||
sebserver.feature.seb.screenProctoring.bundled.url=http://localhost:8090
|
sebserver.feature.seb.screenProctoring.bundled.url=http://localhost:8090
|
||||||
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
||||||
|
|
|
@ -28,6 +28,3 @@ logging.level.com.zaxxer.hikari=INFO
|
||||||
sebserver.http.client.connect-timeout=15000
|
sebserver.http.client.connect-timeout=15000
|
||||||
sebserver.http.client.connection-request-timeout=10000
|
sebserver.http.client.connection-request-timeout=10000
|
||||||
sebserver.http.client.read-timeout=60000
|
sebserver.http.client.read-timeout=60000
|
||||||
|
|
||||||
# features
|
|
||||||
sebserver.feature.seb.screenProctoring.enabled=true
|
|
|
@ -38,6 +38,7 @@ sebserver.webservice.api.admin.clientSecret=${sebserver.password}
|
||||||
sebserver.webservice.internalSecret=${sebserver.password}
|
sebserver.webservice.internalSecret=${sebserver.password}
|
||||||
|
|
||||||
### webservice setup configuration
|
### webservice setup configuration
|
||||||
|
sebserver.webservice.light.setup=false
|
||||||
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
|
||||||
|
@ -92,4 +93,17 @@ sebserver.webservice.api.exam.indicator.thresholds=[{"value":5000.0,"color":"ffc
|
||||||
sebserver.webservice.configtemplate.examconfig.default.name=__startDate__ __examName__
|
sebserver.webservice.configtemplate.examconfig.default.name=__startDate__ __examName__
|
||||||
sebserver.webservice.configtemplate.examconfig.default.description=This has automatically been created from the exam template: __examTemplateName__ at: __currentDate__
|
sebserver.webservice.configtemplate.examconfig.default.description=This has automatically been created from the exam template: __examTemplateName__ at: __currentDate__
|
||||||
|
|
||||||
|
# features
|
||||||
|
sebserver.feature.admin.institution.enabled=true
|
||||||
|
sebserver.feature.seb.liveProctoring.enabled=true
|
||||||
|
sebserver.feature.lms.type.MOCKUP.enabled=true
|
||||||
|
sebserver.feature.exam.noLMS.enabled=true
|
||||||
|
|
||||||
|
sebserver.feature.seb.screenProctoring.enabled=false
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled=true
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.url=sps-service:8090
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.clientPassword=${sps.sebserver.client.secret}
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount
|
||||||
|
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.password=${sps.sebserver.password}
|
||||||
|
|
||||||
|
|
|
@ -62,20 +62,3 @@ security.require-ssl=false
|
||||||
# Disable this if a redirect is done by a reverse proxy for example
|
# Disable this if a redirect is done by a reverse proxy for example
|
||||||
sebserver.ssl.redirect.enabled=false
|
sebserver.ssl.redirect.enabled=false
|
||||||
sebserver.ssl.redirect.html.port=8080
|
sebserver.ssl.redirect.html.port=8080
|
||||||
|
|
||||||
# features
|
|
||||||
sebserver.feature.setup.light.enabled=false
|
|
||||||
sebserver.feature.admin.institution.enabled=true
|
|
||||||
sebserver.feature.seb.remoteProctoring.enabled=true
|
|
||||||
sebserver.feature.lms.testLMS.enabled=true
|
|
||||||
sebserver.feature.exam.noLMS.enabled=true
|
|
||||||
|
|
||||||
sebserver.feature.seb.screenProctoring.enabled=false
|
|
||||||
sebserver.feature.seb.screenProctoring.bundled=true
|
|
||||||
sebserver.feature.seb.screenProctoring.bundled.url=sps-service:8090
|
|
||||||
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
|
|
||||||
sebserver.feature.seb.screenProctoring.bundled.clientPassword=${sps.sebserver.client.secret}
|
|
||||||
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount
|
|
||||||
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.password=${sps.sebserver.password}
|
|
||||||
|
|
||||||
sebserver.feature.CollectingRoomStrategy.SEB-GROUP=false
|
|
||||||
|
|
Loading…
Reference in a new issue