diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java b/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java index 93553f67..cbd73b8a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java @@ -8,6 +8,17 @@ package ch.ethz.seb.sebserver.webservice; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + import ch.ethz.seb.sebserver.SEBServerInit; import ch.ethz.seb.sebserver.WebSecurityConfig; import ch.ethz.seb.sebserver.gbl.client.ClientCredentialServiceImpl; @@ -20,16 +31,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServe import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.InstitutionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; @Component @WebServiceProfile @@ -69,7 +70,7 @@ class AdminUserInitializer { try { log.debug("Create initial admin account is switched on. Check database if exists..."); - final Result byUsername = this.userDAO.sebServerUserByUsername(this.adminName); + final Result byUsername = this.userDAO.sebServerAdminByUsername(this.adminName); if (byUsername.hasValue()) { log.debug("Initial admin account already exists. Check if the password must be reset..."); @@ -130,7 +131,7 @@ class AdminUserInitializer { return account; }) .getOrThrow(); - } + } } catch (final Exception e) { SEBServerInit.INIT_LOGGER.error("---->"); SEBServerInit.INIT_LOGGER.error("----> SEB Server initial admin-account creation failed: ", e); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java index 91322c60..2ab1f59d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInit.java @@ -94,7 +94,7 @@ public class WebserviceInit implements ApplicationListener Ping update time: {}", this.environment.getProperty("sebserver.webservice.distributed.pingUpdate")); SEBServerInit.INIT_LOGGER.info("------> Connection update time: {}", - this.environment.getProperty("sebserver.webservice.distributed.connectionUpdate")); + this.environment.getProperty("sebserver.webservice.distributed.connectionUpdate", "2000")); } try { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java index c6a640c4..32d5bcb6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/UserDAO.java @@ -49,6 +49,12 @@ public interface UserDAO extends ActivatableEntityDAO, BulkAc * @return a Result of SEBServerUser for specified username. Or an exception result on error case */ Result sebServerUserByUsername(String username); + /** Use this to get the SEBServerUser admin principal for a given username. + * + * @param username The username of the user to get SEBServerUser from + * @return a Result of SEBServerUser for specified username. Or an exception result on error case */ + Result sebServerAdminByUsername(String username); + /** Use this to get a Collection containing EntityKey's of all entities that belongs to a given User. * * @param uuid The UUID of the user diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java index 8d1a6ba3..fec7fb1a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java @@ -127,6 +127,21 @@ public class UserDAOImpl implements UserDAO { .flatMap(this::sebServerUserFromRecord); } + @Override + @Transactional(readOnly = true) + public Result sebServerAdminByUsername(final String username) { + return getSingleResource( + username, + this.userRecordMapper + .selectByExample() + .where(UserRecordDynamicSqlSupport.username, isEqualTo(username)) + .and(UserRecordDynamicSqlSupport.active, + isEqualTo(BooleanUtils.toInteger(true))) + .build() + .execute()) + .flatMap(this::sebServerUserFromRecord); + } + @Override @Transactional(readOnly = true) public Result> all(final Long institutionId, final Boolean active) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java index f983f507..36e3e8fd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java @@ -311,7 +311,9 @@ public class ExamAdminServiceImpl implements ExamAdminService { examId, ProctoringServiceSettings.ATTR_ENABLE_PROCTORING) .map(rec -> BooleanUtils.toBoolean(rec.getValue())) - .onError(error -> log.error("Failed to verify proctoring enabled for exam: {}", examId, error)); + .onError(error -> log.warn("Failed to verify proctoring enabled for exam: {}, {}", + examId, + error.getMessage())); if (result.hasError()) { return Result.of(false); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java index 50cc95eb..92b3f77c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java @@ -61,6 +61,8 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato return super.currentValue; } + // TODO do this within a better reactive way like ping updates + try { final Long errors = this.clientEventRecordMapper diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java index 5e9f381c..3fa51e81 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java @@ -67,9 +67,9 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator { return super.currentValue; } - try { + // TODO do this within a better reactive way like ping updates - System.out.println("************** loadFromPersistent"); + try { final List execute = this.clientEventRecordMapper.selectByExample() .where(ClientEventRecordDynamicSqlSupport.clientConnectionId, isEqualTo(this.connectionId)) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java index 0005171b..775be7db 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractPingIndicator.java @@ -19,8 +19,6 @@ import org.springframework.beans.factory.annotation.Qualifier; import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig; import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; -import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType; -import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType; import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.ClientEventLastPingMapper; @@ -35,12 +33,12 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { private final Executor executor; protected final DistributedPingCache distributedPingCache; - //protected Long pingRecord = null; protected PingUpdate pingUpdate = null; protected AbstractPingIndicator( final DistributedPingCache distributedPingCache, @Qualifier(AsyncServiceSpringConfig.EXAM_API_PING_SERVICE_EXECUTOR_BEAN_NAME) final Executor executor) { + super(); this.executor = executor; this.distributedPingCache = distributedPingCache; @@ -81,7 +79,9 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { try { this.executor.execute(this.pingUpdate); } catch (final Exception e) { - //log.warn("Failed to schedule ping task: {}" + e.getMessage()); + if (log.isDebugEnabled()) { + log.warn("Failed to schedule ping task: {}" + e.getMessage()); + } } } } @@ -115,30 +115,6 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { public abstract ClientEventRecord updateLogEvent(final long now); - @Override - public double computeValueAt(final long timestamp) { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void notifyValueChange(final ClientEvent event) { - // TODO Auto-generated method stub - - } - - @Override - public void notifyValueChange(final ClientEventRecord clientEventRecord) { - // TODO Auto-generated method stub - - } - - @Override - public IndicatorType getType() { - // TODO Auto-generated method stub - return null; - } - static final class PingUpdate implements Runnable { private final ClientEventLastPingMapper clientEventLastPingMapper; @@ -151,8 +127,12 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator { @Override public void run() { - this.clientEventLastPingMapper - .updatePingTime(this.pingRecord, Utils.getMillisecondsNow()); + try { + this.clientEventLastPingMapper + .updatePingTime(this.pingRecord, Utils.getMillisecondsNow()); + } catch (final Exception e) { + log.error("Failed to update ping: {}", e.getMessage()); + } } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedPingCache.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedPingCache.java index a80221ca..dd8de44b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedPingCache.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/DistributedPingCache.java @@ -176,6 +176,7 @@ public class DistributedPingCache implements DisposableBean { public Long getLastPing(final Long pingRecordId, final boolean missing) { try { + Long ping = this.pingCache.get(pingRecordId); if (ping == null && !missing) { @@ -199,7 +200,7 @@ public class DistributedPingCache implements DisposableBean { } private void updatePings() { - + if (this.pingCache.isEmpty()) { return; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java index a5bb26fa..7f336f88 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/PingIntervalClientIndicator.java @@ -111,8 +111,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator { @Override public double getValue() { - final long now = DateTimeUtils.currentTimeMillis(); - return now - super.getValue(); + final double value = super.getValue(); + return DateTimeUtils.currentTimeMillis() - value; } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java index 5bf6892e..19af5f4a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/APIExceptionHandler.java @@ -177,6 +177,16 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler { .createErrorResponse(ex.getMessage()); } + @ExceptionHandler(ExamNotRunningException.class) + public ResponseEntity handleExamNotRunning( + final ExamNotRunningException ex, + final WebRequest request) { + + log.warn("{}", ex.getMessage()); + return APIMessage.ErrorMessage.INTEGRITY_VALIDATION + .createErrorResponse(ex.getMessage()); + } + @ExceptionHandler(APIConstraintViolationException.class) public ResponseEntity handleIllegalAPIArgumentException( final APIConstraintViolationException ex, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java index 2302d637..5f8ef958 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamMonitoringController.java @@ -172,8 +172,14 @@ public class ExamMonitoringController { EntityType.EXAM, UserRole.EXAM_SUPPORTER); + // check exam running + final Exam runningExam = this.examSessionService.getRunningExam(examId).getOr(null); + if (runningExam == null) { + throw new ExamNotRunningException("Exam not currently running: " + examId); + } + // check running exam privilege for specified exam - if (!hasRunningExamPrivilege(examId, institutionId)) { + if (!hasRunningExamPrivilege(runningExam, institutionId)) { throw new PermissionDeniedException( EntityType.EXAM, PrivilegeType.READ, @@ -217,8 +223,14 @@ public class ExamMonitoringController { EntityType.EXAM, UserRole.EXAM_SUPPORTER); + // check exam running + final Exam runningExam = this.examSessionService.getRunningExam(examId).getOr(null); + if (runningExam == null) { + throw new ExamNotRunningException("Exam not currently running: " + examId); + } + // check running exam privilege for specified exam - if (!hasRunningExamPrivilege(examId, institutionId)) { + if (!hasRunningExamPrivilege(runningExam, institutionId)) { throw new PermissionDeniedException( EntityType.EXAM, PrivilegeType.READ, @@ -243,6 +255,11 @@ public class ExamMonitoringController { @PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId, @Valid @RequestBody final ClientInstruction clientInstruction) { + // check exam running + if (!this.examSessionService.isExamRunning(examId)) { + throw new ExamNotRunningException("Exam not currently running: " + examId); + } + this.sebClientInstructionService.registerInstruction(clientInstruction); } @@ -261,6 +278,11 @@ public class ExamMonitoringController { @PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId, @PathVariable(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken) { + // check exam running + if (!this.examSessionService.isExamRunning(examId)) { + throw new ExamNotRunningException("Exam not currently running: " + examId); + } + final ClientConnectionData connection = getConnectionDataForSingleConnection( institutionId, examId, @@ -286,6 +308,11 @@ public class ExamMonitoringController { @PathVariable(name = API.PARAM_MODEL_ID, required = true) final Long notificationId, @PathVariable(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken) { + // check exam running + if (!this.examSessionService.isExamRunning(examId)) { + throw new ExamNotRunningException("Exam not currently running: " + examId); + } + this.sebClientNotificationService.confirmPendingNotification( notificationId, examId, @@ -322,12 +349,6 @@ public class ExamMonitoringController { } } - private boolean hasRunningExamPrivilege(final Long examId, final Long institution) { - return hasRunningExamPrivilege( - this.examSessionService.getRunningExam(examId).getOr(null), - institution); - } - private boolean hasRunningExamPrivilege(final Exam exam, final Long institution) { if (exam == null) { return false; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamNotRunningException.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamNotRunningException.java new file mode 100644 index 00000000..640e1311 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamNotRunningException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.weblayer.api; + +public class ExamNotRunningException extends RuntimeException { + + private static final long serialVersionUID = -2931666431463176875L; + + public ExamNotRunningException() { + super(); + } + + public ExamNotRunningException(final String message, final Throwable cause) { + super(message, cause); + } + + public ExamNotRunningException(final String message) { + super(message); + } + +} diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index 88fef4ce..2f583436 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -38,7 +38,7 @@ sebserver.webservice.internalSecret=${sebserver.password} ### webservice networking sebserver.webservice.forceMaster=false -sebserver.webservice.distributed=false +sebserver.webservice.distributed=true sebserver.webservice.distributed.pingUpdate=3000 sebserver.webservice.http.external.scheme=https sebserver.webservice.http.external.servername=