fixes and server init

This commit is contained in:
anhefti 2019-12-09 12:06:14 +01:00
parent 7ce1baafa5
commit 1da72a0f31
11 changed files with 168 additions and 79 deletions

View file

@ -38,7 +38,7 @@ The SEB Server Setup repository contains predefined, docker-based installation d
Install SEB Server
------------------
For a complete guide to install SEB Server please go to `SEB Server Installation Guide <https://seb-server-setup.readthedocs.io/en/latest/#>`_
For a complete guide to install SEB Server please go to `SEB Server Installation Guide <https://seb-server-setup.readthedocs.io/en/latest/overview.html>`_
Getting started with SEB Server
-------------------------------

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gui;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
@Component
@GuiProfile
public class GuiInit implements ApplicationListener<ApplicationReadyEvent> {
static final Logger INIT_LOGGER = LoggerFactory.getLogger("SEB SERVER INIT");
@Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
INIT_LOGGER.info("----> SEB Server GUI Component sucessfully initialized!");
}
}

View file

@ -51,7 +51,7 @@ class AdminUserInitializer {
@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:ETHZ}") final String orgName) {
@Value("${sebserver.init.organisation.name:[SET_ORGANIZATION_NAME]}") final String orgName) {
this.userDAO = userDAO;
this.institutionDAO = institutionDAO;
@ -67,72 +67,78 @@ class AdminUserInitializer {
return;
}
log.debug("Create initial admin account is switched on. Check database if exists...");
final Result<SEBServerUser> byUsername = this.userDAO.sebServerUserByUsername(this.adminName);
if (byUsername.hasValue()) {
try {
log.debug("Initial admin account already exists. Check if the password must be reset...");
log.debug("Create initial admin account is switched on. Check database if exists...");
final Result<SEBServerUser> byUsername = this.userDAO.sebServerUserByUsername(this.adminName);
if (byUsername.hasValue()) {
final SEBServerUser sebServerUser = byUsername.get();
final String password = sebServerUser.getPassword();
if (this.passwordEncoder.matches("admin", password)) {
log.debug("Initial admin account already exists. Check if the password must be reset...");
log.debug("Setting new generated password for already existing admin account");
final SEBServerUser sebServerUser = byUsername.get();
final String password = sebServerUser.getPassword();
if (this.passwordEncoder.matches("admin", password)) {
log.debug("Setting new generated password for already existing admin account");
final CharSequence generateAdminPassword = this.generateAdminPassword();
if (generateAdminPassword != null) {
this.userDAO.changePassword(
sebServerUser.getUserInfo().getModelId(),
generateAdminPassword);
this.writeAdminCredentials(this.adminName, generateAdminPassword);
}
}
} else {
final CharSequence generateAdminPassword = this.generateAdminPassword();
if (generateAdminPassword != null) {
this.userDAO.changePassword(
sebServerUser.getUserInfo().getModelId(),
generateAdminPassword);
this.writeAdminCredentials(this.adminName, generateAdminPassword);
}
}
} else {
final CharSequence generateAdminPassword = this.generateAdminPassword();
if (generateAdminPassword != null) {
Long institutionId = this.institutionDAO.allMatching(new FilterMap())
.getOrElse(() -> Collections.emptyList())
.stream()
.findFirst()
.filter(Institution::isActive)
.map(Institution::getInstitutionId)
.orElseGet(() -> -1L);
if (institutionId < 0) {
log.debug("Create new initial institution");
institutionId = this.institutionDAO.createNew(new Institution(
null,
this.orgName,
null,
null,
null,
true))
.map(inst -> this.institutionDAO.setActive(inst, true).getOrThrow())
Long institutionId = this.institutionDAO.allMatching(new FilterMap())
.getOrElse(() -> Collections.emptyList())
.stream()
.findFirst()
.filter(Institution::isActive)
.map(Institution::getInstitutionId)
.orElseGet(() -> -1L);
if (institutionId < 0) {
log.debug("Create new initial institution");
institutionId = this.institutionDAO.createNew(new Institution(
null,
this.orgName,
null,
null,
null,
true))
.map(inst -> this.institutionDAO.setActive(inst, true).getOrThrow())
.map(Institution::getInstitutionId)
.getOrThrow();
}
this.userDAO.createNew(new UserMod(
this.adminName,
institutionId,
this.adminName,
this.adminName,
generateAdminPassword,
generateAdminPassword,
null,
null,
null,
new HashSet<>(Arrays.asList(UserRole.SEB_SERVER_ADMIN.name()))))
.flatMap(account -> this.userDAO.setActive(account, true))
.map(account -> {
writeAdminCredentials(this.adminName, generateAdminPassword);
return account;
})
.getOrThrow();
}
this.userDAO.createNew(new UserMod(
this.adminName,
institutionId,
this.adminName,
this.adminName,
generateAdminPassword,
generateAdminPassword,
null,
null,
null,
new HashSet<>(Arrays.asList(UserRole.SEB_SERVER_ADMIN.name()))))
.flatMap(account -> this.userDAO.setActive(account, true))
.map(account -> {
writeAdminCredentials(this.adminName, generateAdminPassword);
return account;
})
.getOrThrow();
}
} catch (final Exception e) {
WebserviceInit.INIT_LOGGER.error("---->");
WebserviceInit.INIT_LOGGER.error("----> SEB Server initial admin-account creation failed: ", e);
WebserviceInit.INIT_LOGGER.error("---->");
}
}
private void writeAdminCredentials(final String name, final CharSequence pwd) {

View file

@ -33,6 +33,7 @@ public class WebserviceInfo {
private static final Logger log = LoggerFactory.getLogger(WebserviceInfo.class);
private static final String VERSION_KEY = "sebserver.version";
private static final String WEB_SERVICE_TEST_PROPERTY = "sebserver.test.property";
private static final String WEB_SERVICE_SERVER_NAME_KEY = "sebserver.webservice.http.server.name";
private static final String WEB_SERVICE_HTTP_SCHEME_KEY = "sebserver.webservice.http.scheme";
@ -43,6 +44,7 @@ public class WebserviceInfo {
"sebserver.webservice.api.exam.endpoint.discovery";
private static final String WEB_SERVICE_EXTERNAL_ADDRESS_ALIAS = "sebserver.webservice.lms.address.alias";
private final String sebServerVersion;
private final String testProperty;
private final String httpScheme;
private final String hostAddress; // internal
@ -57,6 +59,7 @@ public class WebserviceInfo {
private Map<String, String> externalAddressAlias;
public WebserviceInfo(final Environment environment) {
this.sebServerVersion = environment.getRequiredProperty(VERSION_KEY);
this.testProperty = environment.getProperty(WEB_SERVICE_TEST_PROPERTY, "NOT_AVAILABLE");
this.httpScheme = environment.getRequiredProperty(WEB_SERVICE_HTTP_SCHEME_KEY);
this.hostAddress = environment.getRequiredProperty(WEB_SERVICE_HOST_ADDRESS_KEY);
@ -100,6 +103,10 @@ public class WebserviceInfo {
}
}
public String getSebServerVersion() {
return this.sebServerVersion;
}
public String getTestProperty() {
return this.testProperty;
}

View file

@ -58,21 +58,24 @@ public class WebserviceInit implements ApplicationListener<ApplicationReadyEvent
INIT_LOGGER.info("---->");
INIT_LOGGER.info("----> SEB Server successfully started up!");
INIT_LOGGER.info("---->");
INIT_LOGGER.info("----> Version: {}", this.webserviceInfo.getSebServerVersion());
try {
INIT_LOGGER.info("----> config server address: {}", this.environment.getProperty("server.address"));
INIT_LOGGER.info("----> config server port: {}", this.environment.getProperty("server.port"));
INIT_LOGGER.info("----> local host address: {}", InetAddress.getLocalHost().getHostAddress());
INIT_LOGGER.info("----> local host name: {}", InetAddress.getLocalHost().getHostName());
INIT_LOGGER.info("----> remote host address: {}", InetAddress.getLoopbackAddress().getHostAddress());
INIT_LOGGER.info("----> remote host name: {}", InetAddress.getLoopbackAddress().getHostName());
INIT_LOGGER.info("----> Server address: {}", this.environment.getProperty("server.address"));
INIT_LOGGER.info("----> Server port: {}", this.environment.getProperty("server.port"));
INIT_LOGGER.info("---->");
INIT_LOGGER.info("----> Local-Host address: {}", InetAddress.getLocalHost().getHostAddress());
INIT_LOGGER.info("----> Local-Host name: {}", InetAddress.getLocalHost().getHostName());
INIT_LOGGER.info("---->");
INIT_LOGGER.info("----> Remote-Host address: {}", InetAddress.getLoopbackAddress().getHostAddress());
INIT_LOGGER.info("----> Remote-Host name: {}", InetAddress.getLoopbackAddress().getHostName());
} catch (final UnknownHostException e) {
log.error("Unknown Host: ", e);
}
INIT_LOGGER.info("----> {}", this.webserviceInfo);
INIT_LOGGER.info("---->");
INIT_LOGGER.info("----> HTTP Scheme {}", this.webserviceInfo.getHttpScheme());
INIT_LOGGER.info("----> Property Override Test: {}", this.webserviceInfo.getTestProperty());
// TODO integration of Flyway for database initialization and migration: https://flywaydb.org
// see also https://flywaydb.org/getstarted/firststeps/api

View file

@ -96,6 +96,12 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
* @return Result refer to the specified exam or to an error if happened */
Result<Long> forceUnlock(Long examId);
/** Used to force unlock all locked exams for a specified updateId
*
* @param updateId the update identifier
* @return list of identifiers of unlocked exams */
Result<Collection<Long>> forceUnlockAll(String updateId);
/** Indicates if the exam with specified identifier has an internal write lock.
*
* @param examId the exam identifier

View file

@ -465,7 +465,9 @@ public class ExamDAOImpl implements ExamDAO {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Result<Long> forceUnlock(final Long examId) {
log.info("forceUnlock for exam: {}", examId);
if (log.isDebugEnabled()) {
log.debug("forceUnlock for exam: {}", examId);
}
return Result.tryCatch(() -> {
final ExamRecord examRecord = new ExamRecord(
@ -481,6 +483,30 @@ public class ExamDAOImpl implements ExamDAO {
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Result<Collection<Long>> forceUnlockAll(final String updateId) {
if (log.isDebugEnabled()) {
log.debug("forceUnlock for updateId: {}", updateId);
}
return Result.tryCatch(() -> {
final Collection<Long> result = this.examRecordMapper.selectIdsByExample()
.where(ExamRecordDynamicSqlSupport.lastupdate, isEqualTo(updateId))
.build()
.execute()
.stream()
.map(this::forceUnlock)
.flatMap(Result::skipOnError)
.collect(Collectors.toList());
return result;
})
.onError(TransactionHandler::rollback);
}
@Override
@Transactional(readOnly = true)
public Result<Boolean> isLocked(final Long examId) {

View file

@ -293,8 +293,12 @@ public class UserDAOImpl implements UserDAO {
.build()
.execute();
return ids.stream()
.map(id -> new EntityKey(id, EntityType.USER))
return this.userRecordMapper.selectByExample()
.where(UserRecordDynamicSqlSupport.id, isIn(ids))
.build()
.execute()
.stream()
.map(record -> new EntityKey(record.getUuid(), EntityType.USER))
.collect(Collectors.toList());
});
}

View file

@ -33,7 +33,6 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
@ -150,7 +149,7 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
return examIdsFirstCheck;
})
.onError(TransactionHandler::rollback);
.onError(t -> this.examDAO.forceUnlockAll(updateId));
}
@Override
@ -180,7 +179,8 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
checkActiveClientConnections(exam);
// lock the exam
this.examDAO.placeLock(exam.id, updateId)
this.examDAO
.placeLock(exam.id, updateId)
.getOrThrow();
// check again if there are no new active client connections in the meantime
@ -188,7 +188,8 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
// apply the referenced change action. On error the change is rolled back and
// this processing returns immediately with the error
final T result = changeAction.apply(mapping)
final T result = changeAction
.apply(mapping)
.onError(t -> log.error("Fauled to save exam configuration: {}",
mapping.configurationNodeId))
.getOrThrow();
@ -205,11 +206,13 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
// flush the exam cache. If there was an error during flush, it is logged but this process goes on
// and the saved changes are not rolled back
this.examSessionService.flushCache(exam)
this.examSessionService
.flushCache(exam)
.onError(t -> log.error("Failed to flush cache for exam: {}", exam));
// release the exam lock
this.examDAO.releaseLock(exam.id, updateId)
this.examDAO
.releaseLock(exam.id, updateId)
.onError(t -> log.error("Failed to release lock for exam: {}", exam));
return result;
@ -238,7 +241,8 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
configurationId);
try {
final Configuration config = this.configurationDAO.byPK(configurationId)
final Configuration config = this.configurationDAO
.byPK(configurationId)
.getOrThrow();
final Collection<Long> involvedExams = this.examConfigurationMapDAO
@ -260,7 +264,8 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
@Override
public Collection<Result<Long>> forceReleaseUpdateLocks(final Collection<Long> examIds) {
return examIds.stream()
return examIds
.stream()
.map(this.examDAO::forceUnlock)
.collect(Collectors.toList());
}

View file

@ -20,6 +20,7 @@ sebserver.http.client.connection-request-timeout=10000
sebserver.http.client.read-timeout=10000
# webservice configuration
sebserver.init.adminaccount.gen-on-init=false
sebserver.webservice.distributed=false
sebserver.webservice.http.scheme=http
sebserver.webservice.http.server.name=${server.address}

View file

@ -79,8 +79,8 @@ public class HTTPClientBot {
public HTTPClientBot(final Map<String, String> args) {
this.webserviceAddress = args.getOrDefault("webserviceAddress", "http://ralph.ethz.ch:8080");
// this.webserviceAddress = args.getOrDefault("webserviceAddress", "http://localhost:8080");
// this.webserviceAddress = args.getOrDefault("webserviceAddress", "http://ralph.ethz.ch:8080");
this.webserviceAddress = args.getOrDefault("webserviceAddress", "http://localhost:8080");
//this.webserviceAddress = args.getOrDefault("webserviceAddress", "https://seb.test-swissmooc.ch");
this.accessTokenEndpoint = args.getOrDefault("accessTokenEndpoint", "/oauth/token");