Merge pull request #112 from SafeExamBrowser/SEBSLI-9

Sebsli 9
This commit is contained in:
NadimETH 2024-04-02 11:08:51 +02:00 committed by GitHub
commit afb2685664
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 415 additions and 65 deletions

View file

@ -94,7 +94,7 @@ jobs:
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
uses: docker/setup-buildx-action@v3.2.0
-
name: Login to DockerHub
uses: docker/login-action@v2

View file

@ -102,6 +102,8 @@ public final class Constants {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public static final Long LIGHT_ADMIN_USER_ID = 1L;
public static final DateTimeFormatter STANDARD_DATE_TIME_MILLIS_FORMATTER = DateTimeFormat
.forPattern(DEFAULT_DATE_TIME_MILLIS_FORMAT)
.withZoneUTC();

View file

@ -126,6 +126,8 @@ public final class API {
public static final String EXAM_API_CONFIGURATION_REQUEST_ENDPOINT = "/examconfig";
public static final String EXAM_API_CONFIGURATION_LIGHT_ENDPOINT = "/light-config";
public static final String EXAM_API_PING_ENDPOINT = "/sebping";
public static final String EXAM_API_PING_TIMESTAMP = "timestamp";

View file

@ -12,6 +12,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
@ -60,4 +61,10 @@ public enum UserRole implements Entity, GrantedAuthority {
}
}
public static List<String> getNamesForAllRoles(){
return Arrays.stream(UserRole.values())
.map(UserRole::getName)
.toList();
}
}

View file

@ -86,6 +86,8 @@ public class GuiWebsecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers(API.ERROR_PATH).permitAll()
.antMatchers(API.CHECK_PATH).permitAll()
.antMatchers(this.examAPIDiscoveryEndpoint).permitAll()
.antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT).permitAll()
.antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT + API.PASSWORD_PATH_SEGMENT).permitAll()
.antMatchers(adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**").permitAll()
.antMatchers(adminAPIEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**").permitAll()
.antMatchers(adminAPIEndpoint + API.REGISTER_ENDPOINT).permitAll()

View file

@ -8,10 +8,16 @@
package ch.ethz.seb.sebserver.webservice;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,26 +46,32 @@ class AdminUserInitializer {
private static final Logger log = LoggerFactory.getLogger(AdminUserInitializer.class);
private final WebserviceInfo webserviceInfo;
private final UserDAO userDAO;
private final InstitutionDAO institutionDAO;
private final PasswordEncoder passwordEncoder;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final boolean initializeAdmin;
private final String adminName;
private final String orgName;
private final Environment environment;
public AdminUserInitializer(
final WebserviceInfo webserviceInfo,
final UserDAO userDAO,
final InstitutionDAO institutionDAO,
final AdditionalAttributesDAO additionalAttributesDAO,
final Environment environment,
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder passwordEncoder,
@Value("${sebserver.init.adminaccount.gen-on-init:false}") final boolean initializeAdmin,
@Value("${sebserver.init.adminaccount.username:seb-server-admin}") final String adminName,
@Value("${sebserver.init.organisation.name:[SET_ORGANIZATION_NAME]}") final String orgName) {
this.webserviceInfo = webserviceInfo;
this.environment = environment;
this.userDAO = userDAO;
this.institutionDAO = institutionDAO;
this.additionalAttributesDAO = additionalAttributesDAO;
this.passwordEncoder = passwordEncoder;
this.initializeAdmin = initializeAdmin;
this.adminName = adminName;
@ -68,7 +80,7 @@ class AdminUserInitializer {
void initAdminAccount() {
if (!this.initializeAdmin) {
log.debug("Create initial admin account is switched on off");
log.debug("Create initial admin account is switched off");
return;
}
@ -90,7 +102,7 @@ class AdminUserInitializer {
this.userDAO.changePassword(
sebServerUser.getUserInfo().getModelId(),
generateAdminPassword);
this.writeAdminCredentials(this.adminName, generateAdminPassword);
this.printAdminCredentials(this.adminName, generateAdminPassword);
}
}
} else {
@ -133,10 +145,16 @@ class AdminUserInitializer {
null,
null,
null,
new HashSet<>(Arrays.asList(UserRole.SEB_SERVER_ADMIN.name()))))
new HashSet<>(this.webserviceInfo.isLightSetup() ?
UserRole.getNamesForAllRoles() :
List.of(UserRole.SEB_SERVER_ADMIN.name())
)))
.flatMap(account -> this.userDAO.setActive(account, true))
.map(account -> {
writeAdminCredentials(this.adminName, generateAdminPassword);
printAdminCredentials(this.adminName, generateAdminPassword);
if(this.webserviceInfo.isLightSetup()) {
writeInitialAdminCredentialsIntoDB(this.adminName, generateAdminPassword);
}
return account;
})
.getOrThrow();
@ -148,7 +166,7 @@ class AdminUserInitializer {
}
}
private void writeAdminCredentials(final String name, final CharSequence pwd) {
private void printAdminCredentials(final String name, final CharSequence pwd) {
SEBServerInit.INIT_LOGGER.info("---->");
SEBServerInit.INIT_LOGGER.info(
"----> ******************************************************************************************"
@ -163,6 +181,23 @@ class AdminUserInitializer {
SEBServerInit.INIT_LOGGER.info("---->");
}
private void writeInitialAdminCredentialsIntoDB(final String name, final CharSequence pwd){
try {
final Map<String, String> attributes = new HashMap<>();
attributes.put(
Domain.USER.ATTR_USERNAME,
name);
attributes.put(
Domain.USER.ATTR_PASSWORD,
String.valueOf(pwd));
this.additionalAttributesDAO.saveAdditionalAttributes(EntityType.USER, Constants.LIGHT_ADMIN_USER_ID, attributes);
} catch (final Exception e) {
log.error("Unable to write initial admin credentials into the additional attributes table: ", e);
}
}
private CharSequence generateAdminPassword() {
try {
return ClientCredentialServiceImpl.generateClientSecret();

View file

@ -102,14 +102,16 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
SEBServerInit.INIT_LOGGER.info("----> Initialize Services...");
SEBServerInit.INIT_LOGGER.info("----> ");
this.applicationEventPublisher.publishEvent(new SEBServerInitEvent(this));
// Run the database integrity checks and fixes if configured
this.dbIntegrityChecker.checkIntegrity();
// Create an initial admin account if requested and not already in the database
this.adminUserInitializer.initAdminAccount();
//emits SEBServerInitEvent
this.applicationEventPublisher.publishEvent(new SEBServerInitEvent(this));
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");
SEBServerInit.INIT_LOGGER.info("----> *** Webservice Info: ***");
SEBServerInit.INIT_LOGGER.info("----> *********************************************************");

View file

@ -0,0 +1,78 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.light.impl;
import ch.ethz.seb.sebserver.SEBServerInitEvent;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import java.util.Collection;
@Lazy
@Service
@ConditionalOnExpression("'${sebserver.webservice.light.setup}'.equals('true')")
public class LightInit {
private final SEBClientConfigDAO sebClientConfigDAO;
public LightInit(final SEBClientConfigDAO sebClientConfigDAO){
this.sebClientConfigDAO = sebClientConfigDAO;
}
@EventListener(SEBServerInitEvent.class)
public void init() {
if(isConnectionConfigAbsent()){
this.sebClientConfigDAO.createNew(createLightConnectionConfiguration())
.getOrThrow();
}
}
private boolean isConnectionConfigAbsent() {
Collection<SEBClientConfig> connectionConfigs = this.sebClientConfigDAO
.all(null, null)
.getOrThrow();
if(connectionConfigs.size() == 0){
return true;
}
return false;
}
private SEBClientConfig createLightConnectionConfiguration(){
return new SEBClientConfig(
1L,
1L,
"light-config",
SEBClientConfig.ConfigPurpose.CONFIGURE_CLIENT,
1000L,
SEBClientConfig.VDIType.NO,
null,
null,
null,
false,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
false,
true,
null,
null,
null);
}
}

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.util.Collection;
@ -173,4 +174,8 @@ public interface SEBClientConnectionService {
String ipAddress,
HttpServletResponse response);
void streamLightExamConfig(
String modelId,
HttpServletResponse response) throws IOException;
}

View file

@ -8,23 +8,36 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.security.Principal;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedIndicatorValueService;
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@ -34,25 +47,20 @@ import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedIndicatorValueService;
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Lazy
@Service
@ -79,6 +87,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
private final SecurityKeyService securityKeyService;
private final SEBClientEventBatchService sebClientEventBatchService;
private final SEBClientInstructionService sebClientInstructionService;
private final ClientConfigService clientConfigService;
private final JSONMapper jsonMapper;
private final boolean isDistributedSetup;
@ -92,6 +101,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
final WebserviceInfo webserviceInfo,
final SEBClientEventBatchService sebClientEventBatchService,
final SEBClientInstructionService sebClientInstructionService,
final ClientConfigService clientConfigService,
final JSONMapper jsonMapper) {
this.examSessionService = examSessionService;
@ -105,6 +115,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
this.isDistributedSetup = webserviceInfo.isDistributed();
this.sebClientEventBatchService = sebClientEventBatchService;
this.sebClientInstructionService = sebClientInstructionService;
this.clientConfigService = clientConfigService;
this.jsonMapper = jsonMapper;
}
@ -623,6 +634,42 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
}
}
public void streamLightExamConfig(final String modelId, final HttpServletResponse response) throws IOException{
final ServletOutputStream outputStream = response.getOutputStream();
PipedOutputStream pout;
PipedInputStream pin;
try {
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
this.clientConfigService.exportSEBClientConfiguration(
pout,
modelId,
null);
IOUtils.copyLarge(pin, outputStream);
response.setStatus(HttpStatus.OK.value());
}catch(Exception e){
final APIMessage errorMessage = APIMessage.ErrorMessage.GENERIC.of(e.getMessage());
outputStream.write(Utils.toByteArray(this.jsonMapper.writeValueAsString(errorMessage)));
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
} finally {
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
log.error("error while flushing / closing output stream", e);
}
}
}
private void writeSEBClientErrors(
final HttpServletResponse response,
final Collection<APIMessage> errorMessages) {
@ -938,4 +985,4 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
return (exam != null && exam.lmsSetupId == null && status == ConnectionStatus.READY) ||
status == ConnectionStatus.ACTIVE;
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services / Informatikdienste (ID)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import com.fasterxml.jackson.annotation.JsonCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
@WebServiceProfile
@RestController
@RequestMapping("${sebserver.webservice.api.exam.endpoint.discovery}")
@ConditionalOnExpression("'${sebserver.webservice.light.setup}'.equals('true')")
public class LightController {
private final SEBClientConnectionService sebClientConnectionService;
private final SEBClientConfigDAO sebClientConfigDAO;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final Executor executor;
protected LightController(
final SEBClientConnectionService sebClientConnectionService,
final SEBClientConfigDAO sebClientConfigDAO,
final AdditionalAttributesDAO additionalAttributesDAO,
@Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME) final Executor executor) {
this.sebClientConnectionService = sebClientConnectionService;
this.sebClientConfigDAO = sebClientConfigDAO;
this.additionalAttributesDAO = additionalAttributesDAO;
this.executor = executor;
}
@RequestMapping(
path = API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public CompletableFuture<Void> getLightConfig(
final HttpServletRequest request,
final HttpServletResponse response){
return CompletableFuture.runAsync(
() -> {
try {
this.sebClientConnectionService.streamLightExamConfig("1", response);
} catch (IOException e) {
throw new RuntimeException(e);
}
},
this.executor
);
}
@RequestMapping(
path = API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT + API.PASSWORD_PATH_SEGMENT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public UsernamePasswordView getInitialAdminPassword(
final HttpServletRequest request,
final HttpServletResponse response){
try {
final String username = this.additionalAttributesDAO.getAdditionalAttribute(EntityType.USER, Constants.LIGHT_ADMIN_USER_ID, Domain.USER.ATTR_USERNAME)
.getOrThrow()
.getValue();
final String password = this.additionalAttributesDAO.getAdditionalAttribute(EntityType.USER, Constants.LIGHT_ADMIN_USER_ID, Domain.USER.ATTR_PASSWORD)
.getOrThrow()
.getValue();
return new UsernamePasswordView(
username,
password
);
} catch (final Exception e) {
return new UsernamePasswordView(
"initial username missing or changed",
"inital password missing or changed"
);
}
}
private String getSebClientConfigId() {
return this.sebClientConfigDAO
.allMatching(
new FilterMap().putIfAbsent(
"active",
String.valueOf(true)
),
Utils.truePredicate()
)
.getOrThrow()
.stream()
.toList()
.get(0)
.getModelId();
}
}
record UsernamePasswordView(String username, String password) {
@JsonCreator
UsernamePasswordView {
}
}

View file

@ -8,19 +8,43 @@
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import javax.validation.Valid;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.user.*;
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.UserFeatures;
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.util.Pair;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.FeatureService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint;
import org.mybatis.dynamic.sql.SqlTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.MediaType;
@ -32,35 +56,25 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import ch.ethz.seb.sebserver.WebSecurityConfig;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
import ch.ethz.seb.sebserver.gbl.api.authorization.Privilege;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.UserRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.RevokeTokenEndpoint;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
@WebServiceProfile
@RestController
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.USER_ACCOUNT_ENDPOINT)
public class UserAccountController extends ActivatableEntityController<UserInfo, UserMod> {
private static final Logger log = LoggerFactory.getLogger(UserAccountController.class);
private final ApplicationEventPublisher applicationEventPublisher;
private final UserDAO userDAO;
private final PasswordEncoder userPasswordEncoder;
private final ScreenProctoringService screenProctoringService;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final WebserviceInfo webserviceInfo;
private final FeatureService featureService;
@ -73,6 +87,8 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
final ApplicationEventPublisher applicationEventPublisher,
final BeanValidationService beanValidationService,
final ScreenProctoringService screenProctoringService,
final AdditionalAttributesDAO additionalAttributesDAO,
final WebserviceInfo webserviceInfo,
final FeatureService featureService,
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
@ -86,6 +102,8 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
this.userDAO = userDAO;
this.userPasswordEncoder = userPasswordEncoder;
this.screenProctoringService = screenProctoringService;
this.additionalAttributesDAO = additionalAttributesDAO;
this.webserviceInfo = webserviceInfo;
this.featureService = featureService;
}
@ -188,6 +206,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
.flatMap(this::revokeAccessToken)
.flatMap(e -> this.userActivityLogDAO.log(UserLogActivityType.PASSWORD_CHANGE, e))
.map(this::synchronizeUserWithSPS)
.map(this::removeInitialAdminPasswordFromDB)
.getOrThrow();
}
@ -293,4 +312,21 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
screenProctoringService.synchronizeSPSUser(userInfo.uuid);
return userInfo;
}
private UserInfo removeInitialAdminPasswordFromDB(final UserInfo userInfo){
if(!this.webserviceInfo.isLightSetup()){
return userInfo;
}
try{
this.additionalAttributesDAO.delete(EntityType.USER, Constants.LIGHT_ADMIN_USER_ID, Domain.USER.ATTR_USERNAME);
this.additionalAttributesDAO.delete(EntityType.USER, Constants.LIGHT_ADMIN_USER_ID, Domain.USER.ATTR_PASSWORD);
}catch(final Exception e){
log.error("Unable to delete initial admin credentials from the additional attributes table: ", e);
}
return userInfo;
}
}

View file

@ -82,6 +82,8 @@ public abstract class WebserviceResourceConfiguration extends ResourceServerConf
.antMatchers(API.ERROR_PATH).permitAll()
.antMatchers(API.CHECK_PATH).permitAll()
.antMatchers(this.examAPIDiscoveryEndpoint).permitAll()
.antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT).permitAll()
.antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT + API.PASSWORD_PATH_SEGMENT).permitAll()
.antMatchers(configurerAdapter.apiEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**").permitAll()
.antMatchers(configurerAdapter.apiEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**").permitAll()
.antMatchers(configurerAdapter.apiEndpoint + API.REGISTER_ENDPOINT).permitAll()

View file

@ -27,7 +27,7 @@ sebserver.init.database.integrity.try-fix=true
# webservice setup configuration
sebserver.init.adminaccount.gen-on-init=false
sebserver.webservice.light.setup=false
sebserver.webservice.light.setup=true
sebserver.webservice.distributed=false
#sebserver.webservice.master.delay.threshold=10000
sebserver.webservice.http.external.scheme=http