SEBSERV-497 new impl and prepare for role based features

This commit is contained in:
anhefti 2024-01-18 17:29:51 +01:00
parent aec6bd6c04
commit 7ffef0938f
24 changed files with 325 additions and 235 deletions

View file

@ -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);
}

View file

@ -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("_", "-");
}
}

View file

@ -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";

View file

@ -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);
}
}

View file

@ -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;
} }

View file

@ -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)

View file

@ -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))
; ;
} }

View file

@ -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);

View file

@ -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())))

View file

@ -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 */

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;
} }

View file

@ -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: {}",

View file

@ -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

View file

@ -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);
}

View file

@ -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);
} }

View file

@ -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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}

View file

@ -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