diff --git a/.gitignore b/.gitignore index 2f2568a1..5a6f26fa 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ pom.xml.tag # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* /log-byDay_IS_UNDEFINED.txt +/log/ diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index ebead99b..690c284e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -104,7 +104,9 @@ public final class API { public static final String EXAM_API_PARAM_EXAM_ID = "examId"; - public static final String EXAM_API_SEB_CONNECTION_TOKEN = "seb-connection-token"; + public static final String EXAM_API_SEB_CONNECTION_TOKEN = "SEBConnectionToken"; + + public static final String EXAM_API_USER_SESSION_ID = "seb_user_session_id"; public static final String EXAM_API_HANDSHAKE_ENDPOINT = "/handshake"; @@ -112,6 +114,10 @@ public final class API { public static final String EXAM_API_PING_ENDPOINT = "/sebping"; + public static final String EXAM_API_PING_TIMESTAMP = "timestamp"; + + public static final String EXAM_API_PING_NUMBER = "ping-number"; + public static final String EXAM_API_EVENT_ENDPOINT = "/seblog"; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExam.java index 1eceb939..5ff163cc 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/RunningExam.java @@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.gbl.model.session; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; + public final class RunningExam { @JsonProperty("examId") @@ -31,6 +33,12 @@ public final class RunningExam { this.url = url; } + public RunningExam(final Exam exam) { + this.examId = exam.getModelId(); + this.name = exam.name; + this.url = exam.startURL; + } + public String getExamId() { return this.examId; } diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java index dd4384be..48a2c86d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/util/Utils.java @@ -51,7 +51,7 @@ public final class Utils { return Collectors.collectingAndThen( Collectors.toList(), list -> { - if (list == null) { + if (list == null || list.size() == 0) { throw new IllegalStateException( "Expected one elements in the given list but is empty"); } @@ -335,4 +335,8 @@ public final class Utils { return string; } + public static String toSQLWildcard(final String text) { + return (text == null) ? null : Constants.PERCENTAGE + text + Constants.PERCENTAGE; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialService.java index 3441719a..164641dd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialService.java @@ -55,11 +55,11 @@ public interface ClientCredentialService { return encryptClientCredentials(clientIdPlaintext, secretPlaintext, null); } - /** Use this to get a decrypted plain text clientId form given ClientCredentials - * - * @param credentials ClientCredentials containing the clientId to decrypt - * @return decrypted plain text clientId */ - CharSequence getPlainClientId(ClientCredentials credentials); +// /** Use this to get a decrypted plain text clientId form given ClientCredentials +// * +// * @param credentials ClientCredentials containing the clientId to decrypt +// * @return decrypted plain text clientId */ +// CharSequence getPlainClientId(ClientCredentials credentials); /** Use this to get a decrypted plain text secret form given ClientCredentials * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceImpl.java index 0a2f7456..c29b0644 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceImpl.java @@ -64,9 +64,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService { .getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY); return new ClientCredentials( - (clientIdPlaintext != null) - ? encrypt(clientIdPlaintext, secret).toString() - : null, + clientIdPlaintext, (secretPlaintext != null) ? encrypt(secretPlaintext, secret).toString() : null, @@ -75,17 +73,10 @@ public class ClientCredentialServiceImpl implements ClientCredentialService { : null); } - @Override - public CharSequence getPlainClientId(final ClientCredentials credentials) { - if (credentials == null || !credentials.hasClientId()) { - return null; - } - - final CharSequence secret = this.environment - .getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY); - - return this.decrypt(credentials.clientId, secret); - } +// @Override +// public CharSequence getPlainClientId(final ClientCredentials credentials) { +// return credentials.clientId; +// } @Override public CharSequence getPlainClientSecret(final ClientCredentials credentials) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java index ff52c5dc..ddad7435 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java @@ -15,4 +15,6 @@ public interface ClientConnectionDAO extends EntityDAO byConnectionToken(Long institutionId, String connectionToken); + Result byConnectionToken(String connectionToken); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java index a58ecaf5..93730344 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ExamConfigurationMapDAO.java @@ -9,10 +9,15 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao; import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; +import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; public interface ExamConfigurationMapDAO extends EntityDAO, BulkActionSupportDAO { + public Result getDefaultConfigurationForExam(Long examId); + + public Result getUserConfigurationIdForExam(final Long examId, final String userId); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java index 16556691..b2044dee 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/FilterMap.java @@ -13,7 +13,6 @@ import org.joda.time.DateTimeZone; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; @@ -179,11 +178,7 @@ public class FilterMap extends POSTMapper { } public String getSQLWildcard(final String name) { - return toSQLWildcard(this.params.getFirst(name)); - } - - public static String toSQLWildcard(final String text) { - return (text == null) ? null : Constants.PERCENTAGE + text + Constants.PERCENTAGE; + return Utils.toSQLWildcard(this.params.getFirst(name)); } public Long getClientConnectionExamId() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SebClientConfigDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SebClientConfigDAO.java index 98905a0d..04c1cc5c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SebClientConfigDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/SebClientConfigDAO.java @@ -18,6 +18,12 @@ public interface SebClientConfigDAO extends ActivatableEntityDAO, BulkActionSupportDAO { + /** Get a SebClientConfig by specified client identifier + * + * @param clientId the client identifier + * @return Result refer to the SebClientConfig for client or refer to an error if happened */ + Result byClientId(String clientId); + /** Get the configured ClientCredentials for a given SebClientConfig. * The ClientCredentials are still encoded as they are on DB storage * diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java index 3f263522..c174ac22 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java @@ -120,7 +120,7 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { data.institutionId, data.examId, ConnectionStatus.CONNECTION_REQUESTED.name(), - null, + data.connectionToken, null, data.clientAddress, data.virtualClientAddress); @@ -214,6 +214,31 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .flatMap(ClientConnectionDAOImpl::toDomainModel); } + @Override + public Result byConnectionToken(final String connectionToken) { + return Result.tryCatch(() -> { + final List list = this.clientConnectionRecordMapper + .selectByExample() + .where( + ClientConnectionRecordDynamicSqlSupport.connectionToken, + SqlBuilder.isEqualTo(connectionToken)) + + .build() + .execute(); + + if (list.isEmpty()) { + throw new ResourceNotFoundException(EntityType.CLIENT_CONNECTION, "connectionToken"); + } + + if (list.size() > 1) { + throw new IllegalStateException("Only one ClientConnection expected but there are: " + list.size()); + } + + return list.get(0); + }) + .flatMap(ClientConnectionDAOImpl::toDomainModel); + } + private Result recordById(final Long id) { return Result.tryCatch(() -> { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index 71934c09..03397173 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -34,6 +34,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus; 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.datalayer.batis.mapper.ConfigurationNodeRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamConfigurationMapRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamConfigurationMapRecordMapper; @@ -125,6 +126,39 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .collect(Collectors.toList())); } + @Override + @Transactional(readOnly = true) + public Result getDefaultConfigurationForExam(final Long examId) { + return Result.tryCatch(() -> this.examConfigurationMapRecordMapper + .selectIdsByExample() + .where( + ExamConfigurationMapRecordDynamicSqlSupport.examId, + SqlBuilder.isEqualTo(examId)) + .and( + ExamConfigurationMapRecordDynamicSqlSupport.userNames, + SqlBuilder.isNull()) + .build() + .execute() + .stream() + .collect(Utils.toSingleton())); + } + + @Override + public Result getUserConfigurationIdForExam(final Long examId, final String userId) { + return Result.tryCatch(() -> this.examConfigurationMapRecordMapper + .selectIdsByExample() + .where( + ExamConfigurationMapRecordDynamicSqlSupport.examId, + SqlBuilder.isEqualTo(examId)) + .and( + ExamConfigurationMapRecordDynamicSqlSupport.userNames, + SqlBuilder.isLike(Utils.toSQLWildcard(userId))) + .build() + .execute() + .stream() + .collect(Utils.toSingleton())); + } + @Override @Transactional public Result createNew(final ExamConfigurationMap data) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java index 59111aaa..b9e68c74 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/LmsSetupDAOImpl.java @@ -318,15 +318,13 @@ public class LmsSetupDAOImpl implements LmsSetupDAO { record.getLmsClientsecret(), record.getLmsRestApiToken()); - final CharSequence plainClientId = this.clientCredentialService.getPlainClientId(clientCredentials); final CharSequence plainAccessToken = this.clientCredentialService.getPlainAccessToken(clientCredentials); - return Result.tryCatch(() -> new LmsSetup( record.getId(), record.getInstitutionId(), record.getName(), LmsType.valueOf(record.getLmsType()), - Utils.toString(plainClientId), + Utils.toString(clientCredentials.clientId), null, record.getLmsUrl(), Utils.toString(plainAccessToken), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java index 91156ad2..6fb0d1b5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/SebClientConfigDAOImpl.java @@ -35,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; 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.datalayer.batis.mapper.SebClientConfigRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.SebClientConfigRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.SebClientConfigRecord; @@ -131,6 +132,24 @@ public class SebClientConfigDAOImpl implements SebClientConfigDAO { }); } + @Override + public Result byClientId(final String clientId) { + return Result.tryCatch(() -> { + + return this.sebClientConfigRecordMapper + .selectByExample() + .where( + SebClientConfigRecordDynamicSqlSupport.clientName, + isEqualTo(clientId)) + .build() + .execute() + .stream() + .map(SebClientConfigDAOImpl::toDomainModel) + .flatMap(DAOLoggingSupport::logAndSkipOnError) + .collect(Utils.toSingleton()); + }); + } + @Override @Transactional(readOnly = true) public boolean isActive(final String modelId) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java index 3b49fa54..16c0e4e0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java @@ -58,7 +58,7 @@ public class LmsAPIServiceImpl implements LmsAPIService { final LmsSetupDAO lmsSetupDAO, final ClientCredentialService clientCredentialService, final ClientHttpRequestFactory clientHttpRequestFactory, - @Value("${sebserver.lms.openedix.api.token.request.paths}") final String alternativeTokenRequestPaths) { + @Value("${sebserver.webservice.lms.openedx.api.token.request.paths}") final String alternativeTokenRequestPaths) { this.asyncService = asyncService; this.lmsSetupDAO = lmsSetupDAO; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/MockupLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/MockupLmsAPITemplate.java index 62e83dc0..c19e289e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/MockupLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/MockupLmsAPITemplate.java @@ -130,7 +130,7 @@ final class MockupLmsAPITemplate implements LmsAPITemplate { private boolean authenticate() { try { - final CharSequence plainClientId = this.clientCredentialService.getPlainClientId(this.credentials); + final CharSequence plainClientId = this.credentials.clientId; if (plainClientId == null || plainClientId.length() <= 0) { throw new IllegalAccessException("Wrong client credential"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java index dca778d2..97df31cd 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java @@ -202,7 +202,7 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { final ClientCredentials credentials, final String accessTokenRequestPath) { - final CharSequence plainClientId = this.clientCredentialService.getPlainClientId(credentials); + final CharSequence plainClientId = credentials.clientId; final CharSequence plainClientSecret = this.clientCredentialService.getPlainClientSecret(credentials); final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebClientConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebClientConfigService.java index 7d5ebdbf..993ca1e7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebClientConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebClientConfigService.java @@ -73,6 +73,6 @@ public interface SebClientConfigService { * * @param clientId the clientId/clientName * @return encoded clientSecret for that SebClientConfiguration with clientId or null of not existing */ - Result getEncodedClientSecret(String clientId); + Result getEncodedClientSecret(String clientId); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java index 316130a6..3380c300 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java @@ -13,6 +13,7 @@ import java.io.OutputStream; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; +import ch.ethz.seb.sebserver.gbl.util.Result; /** The base interface and service for all SEB Exam Configuration related functionality. */ public interface SebExamConfigService { @@ -31,6 +32,10 @@ public interface SebExamConfigService { * @throws FieldValidationException on validation exception */ void validate(ConfigurationTableValues tableValue) throws FieldValidationException; + Result getDefaultConfigurationIdForExam(Long examId); + + Result getUserConfigurationIdForExam(Long examId, String userId); + /** Used to export a specified SEB Exam Configuration as plain XML * This exports the values of the follow-up configuration defined by a given * ConfigurationNode (configurationNodeId) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java index 6587062f..58224da7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebClientConfigServiceImpl.java @@ -17,8 +17,6 @@ import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.UUID; -import javax.servlet.http.HttpServletRequest; - import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -28,14 +26,10 @@ import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.util.UriComponentsBuilder; 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.model.institution.Institution; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SebClientConfig; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; @@ -135,46 +129,14 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { } @Override - public Result getEncodedClientSecret(final String clientId) { - return Result.tryCatch(() -> { - final Collection clientConfigs = this.sebClientConfigDAO.all(extractInstitution(), true) - .getOrThrow(); - - final ClientCredentials clientCredentials = findClientCredentialsFor(clientId, clientConfigs); - return this.clientPasswordEncoder.encode( - this.clientCredentialService.getPlainClientSecret(clientCredentials)); - - }); + public Result getEncodedClientSecret(final String clientId) { + return this.sebClientConfigDAO.byClientId(clientId) + .flatMap(this::getEncodedSecret); } - public ClientCredentials findClientCredentialsFor(final String clientId, - final Collection clientConfigs) { - for (final SebClientConfig config : clientConfigs) { - try { - final ClientCredentials clientCredentials = - this.sebClientConfigDAO.getSebClientCredentials(config.getModelId()) - .getOrThrow(); - if (clientId.equals(this.clientCredentialService.getPlainClientId(clientCredentials))) { - return clientCredentials; - } - } catch (final Exception e) { - log.error("Unexpected error while trying to fetch client credentials: ", e); - } - } - - return null; - } - - private Long extractInstitution() { - try { - final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); - return Long.parseLong(request.getParameter(API.PARAM_INSTITUTION_ID)); - } catch (final Exception e) { - log.error( - "Failed to extract institution from current request. Search client Id over all active client configurations"); - return null; - } + private Result getEncodedSecret(final SebClientConfig clientConfig) { + return this.sebClientConfigDAO.getSebClientCredentials(clientConfig.getModelId()) + .map(cc -> this.clientPasswordEncoder.encode(this.clientCredentialService.getPlainClientSecret(cc))); } @Override @@ -193,8 +155,7 @@ public class SebClientConfigServiceImpl implements SebClientConfigService { .getConfigPasswortCipher(config.getModelId()) .getOrThrow(); - final CharSequence plainClientId = this.clientCredentialService - .getPlainClientId(sebClientCredentials); + final CharSequence plainClientId = sebClientCredentials.clientId; final CharSequence plainClientSecret = this.clientCredentialService .getPlainClientSecret(sebClientCredentials); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java index 0d0894ac..afd65fb7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java @@ -25,7 +25,9 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService; @@ -38,15 +40,18 @@ public class SebExamConfigServiceImpl implements SebExamConfigService { private final ExamConfigIO examConfigIO; private final ConfigurationAttributeDAO configurationAttributeDAO; + private final ExamConfigurationMapDAO examConfigurationMapDAO; private final Collection validators; protected SebExamConfigServiceImpl( final ExamConfigIO examConfigIO, final ConfigurationAttributeDAO configurationAttributeDAO, + final ExamConfigurationMapDAO examConfigurationMapDAO, final Collection validators) { this.examConfigIO = examConfigIO; this.configurationAttributeDAO = configurationAttributeDAO; + this.examConfigurationMapDAO = examConfigurationMapDAO; this.validators = validators; } @@ -122,6 +127,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService { } + @Override + public Result getDefaultConfigurationIdForExam(final Long examId) { + return this.examConfigurationMapDAO.getDefaultConfigurationForExam(examId); + } + + @Override + public Result getUserConfigurationIdForExam(final Long examId, final String userId) { + return this.examConfigurationMapDAO.getUserConfigurationIdForExam(examId, userId); + } + @Override public void exportForExam(final OutputStream out, final Long configExamMappingId) { // TODO Auto-generated method stub diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java index 9a2d5dce..0facec23 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/EventHandlingStrategy.java @@ -12,6 +12,8 @@ import java.util.function.Consumer; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; +/** A exam session SEB client event handling strategy implements a certain strategy to + * store ClientEvent that are coming in within the specified endpoint in height frequency. */ public interface EventHandlingStrategy extends Consumer { String EVENT_CONSUMER_STRATEGY_CONFIG_PROPERTY_KEY = "sebserver.webservice.api.exam.event-handling-strategy"; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java index 626f6e2f..ab379b9e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamSessionService.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session; +import java.io.OutputStream; import java.util.Collection; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; @@ -38,4 +39,6 @@ public interface ExamSessionService { * happened. */ Result> getRunningExamsForInstitution(Long institutionId); + void streamDefaultExamConfig(Long institutionId, String connectionToken, OutputStream out); + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SebClientConnectionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SebClientConnectionService.java index dd5b63aa..292bcaec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SebClientConnectionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/SebClientConnectionService.java @@ -12,24 +12,95 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.util.Result; +/** Service interface defining functionality to handle SEB client connections on running exams. */ public interface SebClientConnectionService { + /** If a SEB client connects to the SEB Server the first time for a exam session, + * this is used to create a ClientConnection for this connection attempt. + * So this starts the SEB Client - SEB Server handshake. + *

+ * The examId is not mandatory here an can still be null if at this time + * no exam was selected. + *

+ * A connection-token to identify the connection is generated and stored within the + * returned ClientConnection. + * + * @param institutionId The institution identifier + * @param clientAddress The clients remote IP address + * @param examId the exam identifier (can be null) + * @return A Result refer to the newly created ClientConnection in state: CONNECTION_REQUESTED, or refer to an error + * if happened */ Result createClientConnection( Long institutionId, String clientAddress, Long examId); - Result establishClientConnection( - final Long institutionId, + /** This updates an already existing ClientConnection with the given connectionToken. + *

+ * If a clientAddress is given and it differs from the existing clientAddress and the there is + * an exam mapping to an exam of type VDI, the given clientAddress is used to update the virtualClientAddress + * of the ClientConnection. + *

+ * If an examId is given this is used to update the ClientConnection + *

+ * If a userSessionId is given this is used to update the ClientConnection + * + * @param connectionToken The connection-token that was given on ClientConnection creation and that identifies the + * connection + * @param institutionId The institution identifier + * @param clientAddress The clients remote IP address + * @param examId The exam identifier + * @param userSessionId The user session identifier of the users http-session with the LMS + * @return A Result refer to the updated ClientConnection instance, or refer to an error if happened */ + Result updateClientConnection( String connectionToken, + Long institutionId, + String clientAddress, Long examId, - final String clientAddress, String userSessionId); - Result closeConnection(String connectionToken); + /** This is used to establish a already created ClientConnection and set it to sate: ESTABLISHED + * The connectionToken identifies the ClientConnection and the given clientAddress must match with + * the clientAddress of the already created ClientConnection in state CONNECTION_REQUESTED. + *

+ * This may not be the case for VDI exams. In case of VDI exams the different clientAddress is stored + * in the virtualClientAddress field of the ClientConnection. + *

+ * The examId may also be null here if the examId is already known within the existing ClientConnection. + * If not, an error is thrown and send to the calling SEB Client. + *

+ * If a userSessionId is provided within the establish request, this is also stored within the ClientConnection. + * + * @param connectionToken The connection-token that was given on ClientConnection creation and that identifies the + * connection + * @param institutionId The institution identifier + * @param examId The exam identifier (may be null of already known) + * @param clientAddress The clients remote IP address + * @param userSessionId The user session identifier of the users http-session with the LMS + * @return A Result refer to the established ClientConnection instance, or refer to an error if happened */ + Result establishClientConnection( + String connectionToken, + Long institutionId, + Long examId, + String clientAddress, + String userSessionId); - void notifyPing(Long connectionId, long timestamp, int pingNumber); + /** This is used to regular close an established ClientConnection from SEB Client side. + *

+ * This will save the existing established ClientConnection in new CLOSED state and flush all caches. + * + * @param connectionToken The connection-token that was given on ClientConnection creation and that identifies the + * connection + * @param institutionId institution identifier + * @param clientAddress The clients remote IP address + * @return A Result refer to the closed ClientConnection instance, or refer to an error if happened */ + Result closeConnection( + String connectionToken, + Long institutionId, + String clientAddress); - void notifyClientEvent(final ClientEvent event, Long connectionId); + void notifyPing(String connectionToken, long timestamp, int pingNumber); + + void notifyClientEvent(String connectionToken, final ClientEvent event); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java index ea08ff5a..fd183323 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ClientIndicatorFactory.java @@ -42,7 +42,7 @@ public class ClientIndicatorFactory { public ClientIndicatorFactory( final ApplicationContext applicationContext, final IndicatorDAO indicatorDAO, - @Value("${sebserver.indicator.caching}") final boolean enableCaching) { + @Value("${sebserver.webservice.api.exam.enable-indicator-cache:true}") final boolean enableCaching) { this.applicationContext = applicationContext; this.indicatorDAO = indicatorDAO; @@ -60,9 +60,15 @@ public class ClientIndicatorFactory { for (final Indicator indicatorDef : examIndicators) { try { + final ClientIndicator indicator = this.applicationContext .getBean(indicatorDef.type.name(), ClientIndicator.class); - indicator.init(indicatorDef, clientConnection.id, this.enableCaching); + + indicator.init( + indicatorDef, + clientConnection.id, + this.enableCaching); + result.add(indicator); } catch (final Exception e) { log.warn("No Indicator with type: {} found as registered bean. Ignore this one.", indicatorDef.type, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java index e0dc8aa2..92450035 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionCacheService.java @@ -8,6 +8,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; @@ -17,6 +24,7 @@ import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; @@ -98,25 +106,34 @@ public class ExamSessionCacheService { @Cacheable( cacheNames = CACHE_NAME_ACTIVE_CLIENT_CONNECTION, - key = "#connectionId", + key = "#connectionToken", unless = "#result == null") - ClientConnectionDataInternal getActiveClientConnection(final Long connectionId) { + ClientConnectionDataInternal getActiveClientConnection(final String connectionToken) { if (log.isDebugEnabled()) { - log.debug("Verify ClientConnection for running exam for caching by id: ", connectionId); + log.debug("Verify ClientConnection for running exam for caching by connectionToken: ", connectionToken); } - final Result byPK = this.clientConnectionDAO.byPK(connectionId); + final Result byPK = this.clientConnectionDAO + .byConnectionToken(connectionToken); + if (byPK.hasError()) { - log.error("Failed to find/load ClientConnection with id {}", connectionId, byPK.getError()); + log.error("Failed to find/load ClientConnection with connectionToken {}", connectionToken, byPK.getError()); return null; } final ClientConnection clientConnection = byPK.get(); + // verify connection is established + if (clientConnection.status != ConnectionStatus.ESTABLISHED) { + log.error("Illegal state: ClientConnection is not in expected state; ESTABLISHED. ClientConnection: ", + clientConnection); + return null; + } + // verify exam is running if (getRunningExam(clientConnection.examId) == null) { - log.error("Exam for ClientConnection with id { is not currently running}", connectionId); + log.error("Exam for ClientConnection with id { is not currently running}", clientConnection.id); return null; } @@ -127,10 +144,10 @@ public class ExamSessionCacheService { @CacheEvict( cacheNames = CACHE_NAME_ACTIVE_CLIENT_CONNECTION, - key = "#connectionId") - void evictClientConnection(final Long connectionId) { + key = "#connectionToken") + void evictClientConnection(final String connectionToken) { if (log.isDebugEnabled()) { - log.debug("Eviction of ClientConnectionData from cache: {}", connectionId); + log.debug("Eviction of ClientConnectionData from cache: {}", connectionToken); } } @@ -139,8 +156,27 @@ public class ExamSessionCacheService { key = "#examId", unless = "#result == null") InMemorySebConfig getDefaultSebConfigForExam(final Long examId) { - // TODO - return null; + final Exam runningExam = this.getRunningExam(examId); + final PipedOutputStream pipOut = new PipedOutputStream(); + try { + final Long configId = this.sebExamConfigService + .getDefaultConfigurationIdForExam(runningExam.id) + .getOrThrow(); + + // TODO add header, zip and encrypt if needed + + final BufferedInputStream in = new BufferedInputStream(new PipedInputStream(pipOut)); + this.sebExamConfigService.exportPlainXML(pipOut, runningExam.institutionId, configId); + + final ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + IOUtils.copyLarge(in, byteOut); + + return new InMemorySebConfig(configId, runningExam.id, byteOut.toByteArray()); + + } catch (final IOException e) { + log.error("Unexpected error while getting default exam configuration for running exam; {}", runningExam, e); + return null; + } } @CacheEvict( diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java index 3ed157ee..cc0110a7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionServiceImpl.java @@ -8,35 +8,46 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; +import java.io.IOException; +import java.io.OutputStream; import java.util.Collection; import java.util.NoSuchElementException; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; +import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +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.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; -import ch.ethz.seb.sebserver.webservice.servicelayer.session.SebClientConnectionService; @Lazy @Service @WebServiceProfile public class ExamSessionServiceImpl implements ExamSessionService { + private static final Logger log = LoggerFactory.getLogger(ExamSessionServiceImpl.class); + + private final ClientConnectionDAO clientConnectionDAO; private final ExamSessionCacheService examSessionCacheService; private final ExamDAO examDAO; protected ExamSessionServiceImpl( final ExamSessionCacheService examSessionCacheService, final ExamDAO examDAO, - final SebClientConnectionService sebClientConnectionService) { + final ClientConnectionDAO clientConnectionDAO) { this.examSessionCacheService = examSessionCacheService; this.examDAO = examDAO; + this.clientConnectionDAO = clientConnectionDAO; } @Override @@ -47,13 +58,24 @@ public class ExamSessionServiceImpl implements ExamSessionService { @Override public Result getRunningExam(final Long examId) { + if (log.isDebugEnabled()) { + log.debug("Running exam request for exam {}", examId); + } + final Exam exam = this.examSessionCacheService.getRunningExam(examId); if (this.examSessionCacheService.isRunning(exam)) { + if (log.isDebugEnabled()) { + log.debug("Exam {} is running and cached", examId); + } + return Result.of(exam); } else { if (exam != null) { this.examSessionCacheService.evict(exam); } + + log.warn("Exam {} is not currently running", examId); + return Result.ofError(new NoSuchElementException( "No currenlty running exam found for id: " + examId)); } @@ -68,4 +90,55 @@ public class ExamSessionServiceImpl implements ExamSessionService { .collect(Collectors.toList())); } + @Override + public void streamDefaultExamConfig( + final Long institutionId, + final String connectionToken, + final OutputStream out) { + + if (log.isDebugEnabled()) { + log.debug("SEB exam configuration download request, connectionToken: {}", connectionToken); + } + + final ClientConnection connection = this.clientConnectionDAO + .byConnectionToken(institutionId, connectionToken) + .getOrThrow(); + + if (connection == null || connection.status != ConnectionStatus.ESTABLISHED) { + log.warn("SEB exam configuration download request, no active ClientConnection found for token: {}", + connectionToken); + throw new AccessDeniedException("Illegal connection token. No active ClientConnection found for token"); + } + + if (log.isDebugEnabled()) { + log.debug("SEB exam configuration download request: {}", connection); + log.debug("Trying to get exam form InMemorySebConfig"); + } + + final InMemorySebConfig sebConfigForExam = this.examSessionCacheService + .getDefaultSebConfigForExam(connection.examId); + + if (log.isDebugEnabled()) { + if (sebConfigForExam == null) { + log.debug("Failed to get and cache InMemorySebConfig for connection: {}", connection); + } + } + + try { + + if (log.isDebugEnabled()) { + log.debug("SEB exam configuration download request, start writing SEB exam configuration"); + } + + out.write(sebConfigForExam.getData()); + + if (log.isDebugEnabled()) { + log.debug("SEB exam configuration download request, finished writing SEB exam configuration"); + } + + } catch (final IOException e) { + log.error("SEB exam configuration download request, failed to write SEB exam configuration: ", e); + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java index 4615cec1..ef217631 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/SebClientConnectionServiceImpl.java @@ -13,10 +13,15 @@ import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; +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.Exam.ExamType; 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.ClientEvent; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrategy; @@ -24,6 +29,9 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.SebClientConnectionService; import io.micrometer.core.instrument.util.StringUtils; +@Lazy +@Service +@WebServiceProfile public class SebClientConnectionServiceImpl implements SebClientConnectionService { private static final Logger log = LoggerFactory.getLogger(SebClientConnectionServiceImpl.class); @@ -65,15 +73,16 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic return Result.tryCatch(() -> { if (log.isDebugEnabled()) { - log.debug("SEB client connection attempt, create ClientConnection for instituion {} and exam: {}", + log.debug("SEB client connection attempt, create ClientConnection for " + + "instituion {} " + + "exam: {} " + + "client address: {}", institutionId, - examId); + examId, + clientAddress); } - // Integrity check: in case examId is provided is the specified exam running? - if (examId != null && !this.examSessionService.isExamRunning(examId)) { - examNotRunningException(examId); - } + checkExamRunning(examId); // Create ClientConnection in status CONNECTION_REQUESTED for further processing final String connectionToken = createToken(); @@ -97,9 +106,69 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic } @Override - public Result establishClientConnection( - final Long institutionId, + public Result updateClientConnection( final String connectionToken, + final Long institutionId, + final String clientAddress, + final Long examId, + final String userSessionId) { + + return Result.tryCatch(() -> { + if (log.isDebugEnabled()) { + log.debug( + "SEB client connection, update ClientConnection for " + + "connectionToken {} " + + "institutionId" + + "exam: {} " + + "client address: {} " + + "userSessionId: {}", + connectionToken, + institutionId, + examId, + clientAddress, + userSessionId); + } + + checkExamRunning(examId); + + final ClientConnection clientConnection = getClientConnection( + connectionToken, + institutionId); + + checkInstitutionalIntegrity( + institutionId, + clientConnection); + + final String virtualClientAddress = getVirtualClientAddress( + (examId != null) ? examId : clientConnection.examId, + clientAddress, + clientConnection.clientAddress); + + final ClientConnection updatedClientConnection = this.clientConnectionDAO + .save(new ClientConnection( + clientConnection.id, + null, + examId, + null, + null, + userSessionId, + null, + virtualClientAddress)) + .getOrThrow(); + + if (log.isDebugEnabled()) { + log.debug("SEB client connection, successfully updated ClientConnection: {}", + updatedClientConnection); + } + + return updatedClientConnection; + }); + } + + @Override + public Result establishClientConnection( + final String connectionToken, + final Long institutionId, final Long examId, final String clientAddress, final String userSessionId) { @@ -108,38 +177,30 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic if (log.isDebugEnabled()) { log.debug( - "SEB client connection, establish ClientConnection for instituion {} and exam: {} and userSessionId: {}", + "SEB client connection, establish ClientConnection for " + + "connectionToken {} " + + "institutionId" + + "exam: {} " + + "client address: {} " + + "userSessionId: {}", + connectionToken, institutionId, examId, + clientAddress, userSessionId); } - // Integrity check: is the specified exam running? - if (!this.examSessionService.isExamRunning(examId)) { - examNotRunningException(examId); - } + checkExamRunning(examId); - final ClientConnection clientConnection = this.clientConnectionDAO - .byConnectionToken(institutionId, connectionToken) - .get(t -> { - // TODO: This indicates some irregularity on SEB-Client connection attempt. - // Later we should handle this more accurately, and maybe indicate this to the monitoring board - // For example; check if there is already a connection for the userIdentifier and - // if true in which state it is. - log.debug("Unable to connect SEB-Client {} to exam {}", - clientAddress, - this.examSessionService.getRunningExam(examId).map(exam -> exam.name)); - throw new IllegalStateException("Unable to connect SEB-Client to exam"); - }); + final ClientConnection clientConnection = getClientConnection( + connectionToken, + institutionId); - // Integrity checks: - if (!institutionId.equals(clientConnection.institutionId)) { - log.error("Instituion integrity violation with institution: {} on clientConnection: {}", - institutionId, - clientConnection); - throw new IllegalAccessError("Instituion integrity violation"); - } + checkInstitutionalIntegrity( + institutionId, + clientConnection); + // Exam integrity if (clientConnection.examId != null && !examId.equals(clientConnection.examId)) { log.error("Exam integrity violation with examId: {} on clientConnection: {}", examId, @@ -147,30 +208,111 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic throw new IllegalAccessError("Exam integrity violation"); } - final ClientConnection updatedClientConnection = this.clientConnectionDAO.save(new ClientConnection( + final String virtualClientAddress = getVirtualClientAddress( + (examId != null) ? examId : clientConnection.examId, + clientAddress, + clientConnection.clientAddress); + + final ClientConnection establishedClientConnection = new ClientConnection( clientConnection.id, - clientConnection.institutionId, - clientConnection.examId, + null, + examId, ClientConnection.ConnectionStatus.ESTABLISHED, null, userSessionId, null, - null)).getOrThrow(); + virtualClientAddress); + + // ClientConnection integrity + if (establishedClientConnection.institutionId == null || + establishedClientConnection.examId == null || + establishedClientConnection.clientAddress == null || + establishedClientConnection.connectionToken == null) { + + log.error("ClientConnection integrity violation: {}", establishedClientConnection); + throw new IllegalStateException("ClientConnection integrity violation: " + establishedClientConnection); + } + + final ClientConnection updatedClientConnection = this.clientConnectionDAO + .save(establishedClientConnection) + .getOrThrow(); + + if (updatedClientConnection.status == ConnectionStatus.ESTABLISHED) { + // load into cache... + final ClientConnectionDataInternal activeClientConnection = this.examSessionCacheService + .getActiveClientConnection(updatedClientConnection.connectionToken); + + if (activeClientConnection == null) { + log.warn("Unable to access and cache ClientConnection"); + } + + if (log.isDebugEnabled()) { + log.debug("ClientConnection: {} successfully established", clientConnection); + } + } else { + if (log.isDebugEnabled()) { + log.debug("ClientConnection: {} updated", clientConnection); + } + } return updatedClientConnection; }); } @Override - public Result closeConnection(final String connectionToken) { - // TODO Auto-generated method stub - return null; + public Result closeConnection( + final String connectionToken, + final Long institutionId, + final String clientAddress) { + + return Result.tryCatch(() -> { + + if (log.isDebugEnabled()) { + log.debug("SEB client connection: regular close attempt for " + + "instituion {} " + + "client address: {} " + + "connectionToken {} ", + institutionId, + clientAddress, + connectionToken); + } + + final ClientConnection clientConnection = this.clientConnectionDAO + .byConnectionToken(institutionId, connectionToken) + .getOrThrow(); + + // evict ClientConnection from cache + this.examSessionCacheService + .evictClientConnection(clientConnection.connectionToken); + + final ClientConnection updatedClientConnection = this.clientConnectionDAO.save(new ClientConnection( + clientConnection.id, + null, + null, + ClientConnection.ConnectionStatus.CLOSED, + null, + null, + null, + null)).getOrThrow(); + + if (log.isDebugEnabled()) { + log.debug("SEB client connection: successfully closed ClientConnection: {}", + clientConnection); + } + + return updatedClientConnection; + }); + } @Override - public void notifyPing(final Long connectionId, final long timestamp, final int pingNumber) { + public void notifyPing( + final String connectionToken, + final long timestamp, + final int pingNumber) { + final ClientConnectionDataInternal activeClientConnection = - this.examSessionCacheService.getActiveClientConnection(connectionId); + this.examSessionCacheService.getActiveClientConnection(connectionToken); if (activeClientConnection != null) { activeClientConnection.pingMappings @@ -180,11 +322,14 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic } @Override - public void notifyClientEvent(final ClientEvent event, final Long connectionId) { + public void notifyClientEvent( + final String connectionToken, + final ClientEvent event) { + this.eventHandlingStrategy.accept(event); final ClientConnectionDataInternal activeClientConnection = - this.examSessionCacheService.getActiveClientConnection(connectionId); + this.examSessionCacheService.getActiveClientConnection(connectionToken); if (activeClientConnection != null) { activeClientConnection.getindicatorMapping(event.eventType) @@ -193,6 +338,30 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic } } + private void checkExamRunning(final Long examId) { + if (examId != null && !this.examSessionService.isExamRunning(examId)) { + examNotRunningException(examId); + } + } + + private ClientConnection getClientConnection(final String connectionToken, final Long institutionId) { + final ClientConnection clientConnection = this.clientConnectionDAO + .byConnectionToken(institutionId, connectionToken) + .getOrThrow(); + return clientConnection; + } + + private void checkInstitutionalIntegrity(final Long institutionId, final ClientConnection clientConnection) + throws IllegalAccessError { + // Institutional integrity + if (!institutionId.equals(clientConnection.institutionId)) { + log.error("Instituion integrity violation with institution: {} on clientConnection: {}", + institutionId, + clientConnection); + throw new IllegalAccessError("Instituion integrity violation"); + } + } + // TODO maybe we need a stronger connectionToken but for now a simple UUID is used private String createToken() { return UUID.randomUUID().toString(); @@ -203,4 +372,30 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic throw new IllegalStateException("The exam " + examId + " is not running"); } + private String getVirtualClientAddress( + final Long examId, + final String requestClientAddress, + final String existingClientAddress) { + + if (examId == null) { + return null; + } + + if (requestClientAddress.equals(existingClientAddress)) { + return null; + } + + if (!isVDI(examId)) { + return null; + } + + return requestClientAddress; + } + + private boolean isVDI(final Long examId) { + return this.examSessionService.getRunningExam(examId) + .getOrThrow() + .getType() == ExamType.VDI; + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java index 27eb4941..66e32749 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceSecurityConfig.java @@ -14,6 +14,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.catalina.filters.RemoteIpFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -94,6 +95,15 @@ public class WebServiceSecurityConfig extends WebSecurityConfigurerAdapter { @Value("${sebserver.webservice.api.redirect.unauthorized}") private String unauthorizedRedirect; + /** Used to get real remote IP address by using "X-Forwarded-For" and "X-Forwarded-Proto" header. + * https://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/filters/RemoteIpFilter.html + * + * @return RemoteIpFilter instance */ + @Bean + public RemoteIpFilter remoteIpFilter() { + return new RemoteIpFilter(); + } + @Bean public AccessTokenConverter accessTokenConverter() { final DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java index b1293f21..3b9c7f3d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/WebServiceUserDetails.java @@ -28,7 +28,6 @@ public class WebServiceUserDetails implements UserDetailsService { this.userDAO = userDAO; } - // TODO do we need an institution id here? otherwise username must be unique thought all institutions! @Override public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { return this.userDAO.sebServerUserByUsername(username) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java index d61eefc1..bfe0f5fb 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamAPI_V1_Controller.java @@ -8,15 +8,22 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.security.Principal; import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -24,43 +31,223 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.POSTMapper; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; +import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent; import ch.ethz.seb.sebserver.gbl.model.session.PingResponse; import ch.ethz.seb.sebserver.gbl.model.session.RunningExam; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; -import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SebClientConfigDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; +import ch.ethz.seb.sebserver.webservice.servicelayer.session.SebClientConnectionService; @WebServiceProfile @RestController @RequestMapping("${sebserver.webservice.api.exam.endpoint.v1}") public class ExamAPI_V1_Controller { + private static final Logger log = LoggerFactory.getLogger(ExamAPI_V1_Controller.class); + + private final ExamDAO examDAO; + private final ExamSessionService examSessionService; + private final SebClientConnectionService sebClientConnectionService; + private final SebClientConfigDAO sebClientConfigDAO; + + protected ExamAPI_V1_Controller( + final ExamDAO examDAO, + final ExamSessionService examSessionService, + final SebClientConnectionService sebClientConnectionService, + final SebClientConfigDAO sebClientConfigDAO) { + + this.examDAO = examDAO; + this.examSessionService = examSessionService; + this.sebClientConnectionService = sebClientConnectionService; + this.sebClientConfigDAO = sebClientConfigDAO; + } + @RequestMapping( path = API.EXAM_API_HANDSHAKE_ENDPOINT, method = RequestMethod.GET, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Collection handshake( - @RequestParam(name = API.PARAM_INSTITUTION_ID, required = true) final Long institutionId, + public Collection handshakeCreate( + @RequestParam(name = API.PARAM_INSTITUTION_ID, required = false) final Long instIdRequestParam, + @RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = false) final Long examIdRequestParam, + @RequestBody final MultiValueMap formParams, + final Principal principal, final HttpServletRequest request, final HttpServletResponse response) { - // TODO - return Arrays.asList(new RunningExam("1", "testExam", "TODO")); + final POSTMapper mapper = new POSTMapper(formParams); + + final String remoteAddr = request.getRemoteAddr(); + final Long institutionId = (instIdRequestParam != null) + ? instIdRequestParam + : mapper.getLong(API.PARAM_INSTITUTION_ID); + final Long examId = (examIdRequestParam != null) + ? examIdRequestParam + : mapper.getLong(API.EXAM_API_PARAM_EXAM_ID); + final Long clientsInstitution = getInstitutionId(principal); + + if (!clientsInstitution.equals(institutionId)) { + log.error("Institutional integrity violation: requested institution: {} authenticated institution: {}", + institutionId, + clientsInstitution); + throw new APIConstraintViolationException("Institutional integrity violation"); + } + + if (log.isDebugEnabled()) { + log.debug("Request received on Exam Client Connection create endpoint: " + + "institution: {} " + + "exam: {} " + + "client-address: {}", + institutionId, + examId, + remoteAddr); + } + + List result; + if (examId == null) { + result = this.examSessionService.getRunningExamsForInstitution(institutionId) + .getOrThrow() + .stream() + .map(exam -> new RunningExam(exam)) + .collect(Collectors.toList()); + } else { + final Exam exam = this.examDAO.byPK(examId) + .getOrThrow(); + + result = Arrays.asList(new RunningExam(exam)); + } + + if (result.isEmpty()) { + log.warn("There are no currently running exams for institution: {}. SEB connection creation denied"); + throw new IllegalStateException("There are no currently running exams"); + } + + final ClientConnection clientConnection = this.sebClientConnectionService + .createClientConnection(institutionId, remoteAddr, examId) + .getOrThrow(); + + response.setHeader( + API.EXAM_API_SEB_CONNECTION_TOKEN, + clientConnection.connectionToken); + + return result; + } + + @RequestMapping( + path = API.EXAM_API_HANDSHAKE_ENDPOINT, + method = RequestMethod.PATCH, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public void handshakeUpdate( + @RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = false) final Long examId, + @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, + @RequestParam(name = API.EXAM_API_USER_SESSION_ID, required = false) final String userSessionId, + final Principal principal, + final HttpServletRequest request) { + + final String remoteAddr = request.getRemoteAddr(); + final Long institutionId = getInstitutionId(principal); + + if (log.isDebugEnabled()) { + log.debug("Request received on SEB Client Connection update endpoint: " + + "institution: {} " + + "exam: {} " + + "userSessionId: {} " + + "client-address: {}", + institutionId, + examId, + userSessionId, + remoteAddr); + } + + this.sebClientConnectionService.establishClientConnection( + connectionToken, + institutionId, + examId, + remoteAddr, + userSessionId) + .getOrThrow(); + } + + @RequestMapping( + path = API.EXAM_API_HANDSHAKE_ENDPOINT, + method = RequestMethod.PUT, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public void handshakeEstablish( + @RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = false) final Long examId, + @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, + @RequestParam(name = API.EXAM_API_USER_SESSION_ID, required = false) final String userSessionId, + final Principal principal, + final HttpServletRequest request) { + + final String remoteAddr = request.getRemoteAddr(); + final Long institutionId = getInstitutionId(principal); + + if (log.isDebugEnabled()) { + log.debug("Request received on SEB Client Connection establish endpoint: " + + "institution: {} " + + "exam: {} " + + "client-address: {}", + institutionId, + examId, + remoteAddr); + } + + this.sebClientConnectionService.establishClientConnection( + connectionToken, + institutionId, + examId, + remoteAddr, + userSessionId) + .getOrThrow(); + } + + @RequestMapping( + path = API.EXAM_API_HANDSHAKE_ENDPOINT, + method = RequestMethod.DELETE, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public void handshakeEstablish( + @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, + final Principal principal, + final HttpServletRequest request) { + + final String remoteAddr = request.getRemoteAddr(); + final Long institutionId = getInstitutionId(principal); + + if (log.isDebugEnabled()) { + log.debug("Request received on SEB Client Connection close endpoint: " + + "institution: {} " + + "client-address: {}", + institutionId, + remoteAddr); + } + + this.sebClientConnectionService.closeConnection( + connectionToken, + institutionId, + remoteAddr) + .getOrThrow(); } @RequestMapping( path = API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT, method = RequestMethod.GET, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, - produces = MediaType.TEXT_XML_VALUE) + produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity getConfig( @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, - @RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = true) final String examId) { + final Principal principal) { + + final Long institutionId = getInstitutionId(principal); + final StreamingResponseBody stream = out -> this.examSessionService.streamDefaultExamConfig( + institutionId, + connectionToken, + out); - // TODO - // 1. check connection validity (connection token) - // 2. get and stream SEB Exam configuration for specified exam (Id) - final StreamingResponseBody stream = out -> out.write(Utils.toByteArray("TODO SEB Config")); return new ResponseEntity<>(stream, HttpStatus.OK); } @@ -68,13 +255,17 @@ public class ExamAPI_V1_Controller { path = API.EXAM_API_PING_ENDPOINT, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, - produces = MediaType.TEXT_XML_VALUE) + produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public PingResponse ping( @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, - final HttpServletRequest request, - final HttpServletResponse response) { + @RequestParam(name = API.EXAM_API_PING_TIMESTAMP, required = true) final long timestamp, + @RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) { + + this.sebClientConnectionService + .notifyPing(connectionToken, timestamp, pingNumber); + + // TODO ping response (issue SEBSERV-74) - // TODO return null; } @@ -84,11 +275,15 @@ public class ExamAPI_V1_Controller { consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public void event( @RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken, - final HttpServletRequest request, - final HttpServletResponse response) { + @RequestBody(required = true) final ClientEvent event) { - // TODO + this.sebClientConnectionService.notifyClientEvent(connectionToken, event); + } + private Long getInstitutionId(final Principal principal) { + final String clientId = principal.getName(); + return this.sebClientConfigDAO.byClientId(clientId) + .getOrThrow().institutionId; } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java index 6bf5bb84..2a23fa0c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/oauth/WebClientDetailsService.java @@ -86,7 +86,7 @@ public class WebClientDetailsService implements ClientDetailsService { ""); baseClientDetails.setScope(Collections.emptySet()); - baseClientDetails.setClientSecret(pwd); + baseClientDetails.setClientSecret(Utils.toString(pwd)); return baseClientDetails; }); } diff --git a/src/main/resources/config/application-demo.properties b/src/main/resources/config/application-demo.properties index 13f27913..13d75a00 100644 --- a/src/main/resources/config/application-demo.properties +++ b/src/main/resources/config/application-demo.properties @@ -25,7 +25,7 @@ sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1 sebserver.webservice.api.redirect.unauthorized=http://0.0.0.0:8080/gui sebserver.webservice.api.pagination.maxPageSize=500 # comma separated list of known possible OpenEdX API access token request endpoints -sebserver.lms.openedix.api.token.request.paths=/oauth2/access_token +sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token sebserver.gui.entrypoint=/gui sebserver.gui.webservice.protocol=http diff --git a/src/main/resources/config/application-dev-ws.properties b/src/main/resources/config/application-dev-ws.properties index 04938194..5c1b2c44 100644 --- a/src/main/resources/config/application-dev-ws.properties +++ b/src/main/resources/config/application-dev-ws.properties @@ -2,6 +2,9 @@ server.address=localhost server.port=8090 server.servlet.context-path=/ +logging.file=log/sebserver.log + +# spring.datasource.initialize=true spring.datasource.initialization-mode=always spring.datasource.url=jdbc:mariadb://localhost:6603/SEBServer?useSSL=false @@ -9,6 +12,7 @@ spring.datasource.driver-class-name=org.mariadb.jdbc.Driver spring.datasource.platform=dev spring.datasource.hikari.max-lifetime=600000 +# webservice configuration sebserver.webservice.http.scheme=http sebserver.webservice.http.server.name=localhost sebserver.webservice.api.admin.endpoint=/admin-api/v1 @@ -20,7 +24,11 @@ sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoi sebserver.webservice.api.exam.accessTokenValiditySeconds=1800 sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1 sebserver.webservice.api.exam.event-handling-strategy=ASYNC_BATCH_STORE_STRATEGY +sebserver.webservice.api.exam.enable-indicator-cache=true sebserver.webservice.api.pagination.maxPageSize=500 +# comma separated list of known possible OpenEdX API access token request endpoints +sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token +# actuator configuration management.endpoints.web.base-path=/actuator - +management.endpoints.web.exposure.include=logfile,loggers \ No newline at end of file diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index ab7bc3e5..992c0cba 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -8,5 +8,3 @@ spring.http.encoding.enabled=true sebserver.version=0.3.0 pre-beta sebserver.supported.languages=en,de -# comma separated list of known possible OpenEdX API access token request endpoints -sebserver.lms.openedix.api.token.request.paths=/oauth2/access_token \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index f54a3ec7..22630cd2 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -7,8 +7,8 @@ - - log-${byDay}.txt + + log/sebserver.log true %d{HH:mm:ss.SSS} %-5level [%thread]:[%logger] %msg%n @@ -19,6 +19,7 @@ + diff --git a/src/main/resources/schema-demo.sql b/src/main/resources/schema-demo.sql index dd162290..97cacf25 100644 --- a/src/main/resources/schema-demo.sql +++ b/src/main/resources/schema-demo.sql @@ -79,18 +79,25 @@ DROP TABLE IF EXISTS `client_connection` ; CREATE TABLE IF NOT EXISTS `client_connection` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `institution_id` BIGINT UNSIGNED NOT NULL, `exam_id` BIGINT UNSIGNED NULL, `status` VARCHAR(45) NOT NULL, `connection_token` VARCHAR(255) NOT NULL, - `exam_user_session_identifer` VARCHAR(255) NOT NULL, + `exam_user_session_identifer` VARCHAR(255) NULL, `client_address` VARCHAR(45) NOT NULL, `virtual_client_address` VARCHAR(45) NULL, PRIMARY KEY (`id`), INDEX `connection_exam_ref_idx` (`exam_id` ASC), + INDEX `clientConnectionInstitutionRef_idx` (`institution_id` ASC), CONSTRAINT `clientConnectionExamRef` FOREIGN KEY (`exam_id`) REFERENCES `exam` (`id`) ON DELETE NO ACTION + ON UPDATE NO ACTION, + CONSTRAINT `clientConnectionInstitutionRef` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) + ON DELETE NO ACTION ON UPDATE NO ACTION) ; diff --git a/src/main/resources/schema-dev.sql b/src/main/resources/schema-dev.sql index cb38c3f7..841b9e5a 100644 --- a/src/main/resources/schema-dev.sql +++ b/src/main/resources/schema-dev.sql @@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS `client_connection` ( `exam_id` BIGINT UNSIGNED NULL, `status` VARCHAR(45) NOT NULL, `connection_token` VARCHAR(255) NOT NULL, - `exam_user_session_identifer` VARCHAR(255) NOT NULL, + `exam_user_session_identifer` VARCHAR(255) NULL, `client_address` VARCHAR(45) NOT NULL, `virtual_client_address` VARCHAR(45) NULL, PRIMARY KEY (`id`), diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceTest.java index 33e3c008..3d13861a 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/client/ClientCredentialServiceTest.java @@ -20,13 +20,12 @@ public class ClientCredentialServiceTest { // @Test // public void testEncryptSimpleSecret() { // final Environment envMock = mock(Environment.class); -// when(envMock.getRequiredProperty(ClientCredentialService.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY)) -// .thenReturn("secret1"); +// when(envMock.getRequiredProperty(ClientCredentialServiceImpl.SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY)) +// .thenReturn("internalSecret"); // -// final ClientCredentialService service = new ClientCredentialService(envMock); -// final String encrypt = service.encrypt("text1"); -// final String decrypt = service.decrypt(encrypt); -// assertEquals("text1", decrypt); +// final ClientCredentialService service = new ClientCredentialServiceImpl(envMock); +// final CharSequence encrypt = service.encrypt("test"); +// assertEquals("", encrypt.toString()); // } @Test diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index 7f9e4f25..e3012f51 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -23,5 +23,7 @@ sebserver.webservice.api.exam.accessTokenValiditySeconds=1800 sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1 sebserver.webservice.internalSecret=TO_SET sebserver.webservice.api.redirect.unauthorized=none +# comma separated list of known possible OpenEdX API access token request endpoints +sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token management.endpoints.web.base-path=/actuator \ No newline at end of file diff --git a/src/test/resources/data-test-additional.sql b/src/test/resources/data-test-additional.sql new file mode 100644 index 00000000..c1998eb7 --- /dev/null +++ b/src/test/resources/data-test-additional.sql @@ -0,0 +1,813 @@ +INSERT IGNORE INTO lms_setup VALUES + (1, 1, 'test', 'MOCKUP', 'http://', 'ccdfa2330533ed6c316a8ffbd64a3197d4a79956ac7ee4c1162f7bdb1a27234fe8793615a51074351e', '8d14b78ecdcbec1d010d414a7208dbe5c411f1fa735c35c7427d840453093a3730d1bc0abe13b9b1a8', null, 1) + ; + +INSERT IGNORE INTO seb_client_configuration VALUES + (1, 1, 'test', '2019-07-02 09:22:50', 'test', '98ac3c953abf5948d9d13c81cab580819ee2624c76d6d4147d4896a5b79f49956d382c08c93cb3b9ae350b32', null, 1) + ; + +INSERT IGNORE INTO exam VALUES + (1, 1, 1, 'quiz1', 'super-admin', 'super-admin', 'MANAGED', null, null, 1), + (2, 1, 1, 'quiz6', 'super-admin', 'super-admin', 'MANAGED', null, null, 1) + ; + +INSERT IGNORE INTO indicator VALUES + (1, 2, 'LAST_PING', 'Ping', 'dcdcdc') + ; + +INSERT IGNORE INTO threshold VALUES + (1, 1, 1000.0000, '22b14c'), + (2, 1, 2000.0000, 'ff7e00'), + (3, 1, 5000.0000, 'ed1c24') + ; + +INSERT IGNORE INTO view VALUES + (1, 'general', 4, 1), + (2, 'user_interface', 12, 2), + (3, 'browser', 12, 3), + (4, 'down_upload', 12, 4), + (5, 'exam', 12, 5), + (6, 'applications', 12, 6), + (7, 'resources', 12, 7), + (8, 'network', 12, 8), + (9, 'security', 12, 9), + (10, 'registry', 12, 10), + (11, 'hooked_keys', 12, 11); + +INSERT IGNORE INTO configuration_attribute VALUES + (1, 'hashedAdminPassword', 'PASSWORD_FIELD', null, null, null, null, null), + (2, 'allowQuit', 'CHECKBOX', null, null, null, null, 'true'), + (3, 'ignoreExitKeys', 'CHECKBOX', null, null, null, null, 'false'), + (4, 'hashedQuitPassword', 'PASSWORD_FIELD', null, null, null, null, null), + (5, 'exitKey1', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7,8,9,10,11', 'ExitKeySequenceValidator', 'resourceLocTextKey=sebserver.examconfig.props.label.exitKey', '2'), + (6, 'exitKey2', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7,8,9,10,11', 'ExitKeySequenceValidator', 'resourceLocTextKey=sebserver.examconfig.props.label.exitKey', '10'), + (7, 'exitKey3', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7,8,9,10,11', 'ExitKeySequenceValidator', 'resourceLocTextKey=sebserver.examconfig.props.label.exitKey', '5'), + + (8, 'browserViewMode', 'RADIO_SELECTION', null, '0,1,2', null, null, '0'), + (9, 'enableTouchExit', 'CHECKBOX', null, null, null, null, 'false'), + (10, 'mainBrowserWindowWidth', 'COMBO_SELECTION', null, '50%,100%,800,1000', 'WindowsSizeValidator', null, '100%'), + (11, 'mainBrowserWindowHeight', 'COMBO_SELECTION', null, '80%,100%,600,800', 'WindowsSizeValidator', null, '100%'), + (12, 'mainBrowserWindowPositioning', 'SINGLE_SELECTION', null, '0,1,2', null, null, '1'), + (13, 'enableBrowserWindowToolbar', 'CHECKBOX', null, null, null, null, 'false'), + (14, 'hideBrowserWindowToolbar', 'CHECKBOX', null, null, null, null, 'false'), + (15, 'showMenuBar', 'CHECKBOX', null, null, null, null, 'false'), + (16, 'showTaskBar', 'CHECKBOX', null, null, null, null, 'true'), + (17, 'taskBarHeight', 'COMBO_SELECTION', null, '40,60,80', 'IntegerTypeValidator', null, '40'), + (18, 'showReloadButton', 'CHECKBOX', null, null, null, null, 'true'), + (19, 'showTime', 'CHECKBOX', null, null, null, null, 'true'), + (20, 'showInputLanguage', 'CHECKBOX', null, null, null, null, 'false'), + (21, 'enableZoomPage', 'CHECKBOX', null, null, null, null, 'true'), + (22, 'enableZoomText', 'CHECKBOX', null, null, null, null, 'true'), + (23, 'zoomMode', 'RADIO_SELECTION', null, '0,1', null, null, '0'), + (24, 'audioControlEnabled', 'CHECKBOX', null, null, null, null, 'false'), + (25, 'audioMute', 'CHECKBOX', null, null, null, null, 'false'), + (26, 'audioSetVolumeLevel', 'CHECKBOX', null, null, null, null, 'false'), + (27, 'audioVolumeLevel', 'SLIDER', null, '0,100', null, null, '25'), + (28, 'allowSpellCheck', 'CHECKBOX', null, null, null, null, 'false'), + (29, 'allowDictionaryLookup', 'CHECKBOX', null, null, null, null, 'false'), + (30, 'allowSpellCheckDictionary', 'MULTI_CHECKBOX_SELECTION', null, 'da-DK,en-AU,en-GB,en-US,es-ES,fr-FR,pt-PT,sv-SE,sv-FI', null, null, 'da-DK,en-AU,en-GB,en-US,es-ES,fr-FR,pt-PT,sv-SE,sv-FI'), + + (31, 'newBrowserWindowByLinkPolicy', 'RADIO_SELECTION', null, '0,1,2', null, null, '2'), + (32, 'newBrowserWindowByLinkBlockForeign', 'CHECKBOX', null, null, null, null, 'false'), + (33, 'newBrowserWindowByLinkWidth', 'COMBO_SELECTION', null, '50%,100%,800,1000', 'WindowsSizeValidator', null, '100%'), + (34, 'newBrowserWindowByLinkHeight', 'COMBO_SELECTION', null, '80%,100%,600,800', 'WindowsSizeValidator', null, '100%'), + (35, 'newBrowserWindowByLinkPositioning', 'SINGLE_SELECTION', null, '0,1,2', null, null, '2'), + (36, 'enablePlugIns', 'CHECKBOX', null, null, null, null, 'true'), + (37, 'enableJavaScript', 'CHECKBOX', null, null, null, null, 'true'), + (38, 'enableJava', 'CHECKBOX', null, null, null, null, 'false'), + (39, 'blockPopUpWindows', 'CHECKBOX', null, null, null, null, 'false'), + (40, 'allowVideoCapture', 'CHECKBOX', null, null, null, null, 'false'), + (41, 'allowAudioCapture', 'CHECKBOX', null, null, null, null, 'false'), + (42, 'allowBrowsingBackForward', 'CHECKBOX', null, null, null, null, 'false'), + (43, 'newBrowserWindowNavigation', 'CHECKBOX', null, null, null, null, 'true'), + (44, 'browserWindowAllowReload', 'CHECKBOX', null, null, null, null, 'true'), + (45, 'newBrowserWindowAllowReload', 'CHECKBOX', null, null, null, null, 'true'), + (46, 'showReloadWarning', 'CHECKBOX', null, null, null, null, 'true'), + (47, 'newBrowserWindowShowReloadWarning', 'CHECKBOX', null, null, null, null, 'false'), + (48, 'removeBrowserProfile', 'CHECKBOX', null, null, null, null, 'false'), + (49, 'removeLocalStorage', 'CHECKBOX', null, null, null, null, 'false'), + (50, 'browserUserAgent', 'TEXT_FIELD', null, null, null, null, null), + (51, 'browserUserAgentWinDesktopMode', 'RADIO_SELECTION', null, '0,1', null, null, '0'), + (52, 'browserUserAgentWinDesktopModeCustom', 'TEXT_FIELD', null, null, null, null, null), + (53, 'browserUserAgentWinTouchMode', 'RADIO_SELECTION', null, '0,1,2', null, null, '0'), + (54, 'browserUserAgentWinTouchModeCustom', 'TEXT_FIELD', null, null, null, null, null), + (55, 'browserUserAgentMac', 'RADIO_SELECTION', null, '0,1', null, null, '0'), + (56, 'browserUserAgentMacCustom', 'TEXT_FIELD', null, null, null, null, null), + (57, 'enableSebBrowser', 'CHECKBOX', null, null, null, null, 'true'), + (58, 'browserWindowTitleSuffix', 'TEXT_FIELD', null, null, null, null, null), + + (59, 'allowDownUploads', 'CHECKBOX', null, null, null, null, 'true'), + (60, 'downloadDirectoryWin', 'TEXT_FIELD', null, null, null, null, null), + (61, 'downloadDirectoryOSX', 'TEXT_FIELD', null, null, null, null, null), + (62, 'openDownloads', 'CHECKBOX', null, null, null, null, 'false'), + (63, 'chooseFileToUploadPolicy', 'RADIO_SELECTION', null, '0,1,2', null, null, '0'), + (64, 'downloadPDFFiles', 'CHECKBOX', null, null, null, null, 'true'), + (65, 'allowPDFPlugIn', 'CHECKBOX', null, null, null, null, 'true'), + (66, 'downloadAndOpenSebConfig', 'CHECKBOX', null, null, null, null, 'true'), + + (67, 'quitURL', 'TEXT_FIELD', null, null, null, null, null), + (68, 'quitURLConfirm', 'CHECKBOX', null, null, null, null, 'true'), + (69, 'restartExamUseStartURL', 'CHECKBOX', null, null, null, null, 'false'), + (70, 'restartExamURL', 'TEXT_FIELD', null, null, null, null, null), + (71, 'restartExamText', 'TEXT_FIELD', null, null, null, null, null), + (72, 'restartExamPasswordProtected', 'CHECKBOX', null, null, null, null, 'true'), + + (73, 'permittedProcesses', 'TABLE', null, null, null, null, null), + (74, 'permittedProcesses.active', 'CHECKBOX', 73, null, null, null, 'true'), + (75, 'permittedProcesses.os', 'SINGLE_SELECTION', 73, '0,1', null, null, '0'), + (76, 'permittedProcesses.title', 'TEXT_FIELD', 73, null, null, null, ''), + (77, 'permittedProcesses.description', 'TEXT_FIELD', 73, null, null, null, ''), + (78, 'permittedProcesses.executable', 'TEXT_FIELD', 73, null, null, null, ''), + (79, 'permittedProcesses.originalName', 'TEXT_FIELD', 73, null, null, null, ''), + (80, 'permittedProcesses.allowedExecutables', 'TEXT_FIELD', 73, null, null, null, ''), + (81, 'permittedProcesses.path', 'TEXT_FIELD', 73, null, null, null, ''), + (82, 'permittedProcesses.arguments', 'INLINE_TABLE', 73, '1:active:CHECKBOX|4:argument:TEXT_FIELD', null, null, null), + (85, 'permittedProcesses.identifier', 'TEXT_FIELD', 73, null, null, null, ''), + (86, 'permittedProcesses.iconInTaskbar', 'CHECKBOX', 73, null, null, null, 'true'), + (87, 'permittedProcesses.autostart', 'CHECKBOX', 73, null, null, null, 'false'), + (88, 'permittedProcesses.runInBackground', 'CHECKBOX', 73, null, null, null, 'false'), + (89, 'permittedProcesses.allowUserToChooseApp', 'CHECKBOX', 73, null, null, null, 'false'), + (90, 'permittedProcesses.strongKill', 'CHECKBOX', 73, null, null, null, 'false'), + (91, 'allowSwitchToApplications', 'CHECKBOX', null, null, null, null, 'false'), + (92, 'allowFlashFullscreen', 'CHECKBOX', null, null, null, null, 'false'), + + (93, 'prohibitedProcesses', 'TABLE', null, null, null, null, null), + (94, 'prohibitedProcesses.active', 'CHECKBOX', 93, null, null, null, 'true'), + (95, 'prohibitedProcesses.os', 'SINGLE_SELECTION', 93, '0,1', null, null, '0'), + (96, 'prohibitedProcesses.executable', 'TEXT_FIELD', 93, null, null, null, ''), + (97, 'prohibitedProcesses.description', 'TEXT_FIELD', 93, null, null, null, ''), + (98, 'prohibitedProcesses.originalName', 'TEXT_FIELD', 93, null, null, null, ''), + (99, 'prohibitedProcesses.identifier', 'TEXT_FIELD', 93, null, null, null, ''), + (100, 'prohibitedProcesses.strongKill', 'CHECKBOX', 93, null, null, null, 'false'), + + (200, 'URLFilterEnable', 'CHECKBOX', null, null, null, null, 'false'), + (201, 'URLFilterEnableContentFilter', 'CHECKBOX', null, null, null, null, 'false'), + (202, 'URLFilterRules', 'TABLE', null, null, null, null, null), + (203, 'URLFilterRules.active', 'CHECKBOX', 202, null, null, null, 'true'), + (204, 'URLFilterRules.regex', 'CHECKBOX', 202, null, null, null, 'false'), + (205, 'URLFilterRules.expression', 'TEXT_FIELD', 202, null, null, null, ''), + (206, 'URLFilterRules.action', 'SINGLE_SELECTION', 202, '0,1', null, null, ''), + + (210, 'proxySettingsPolicy', 'RADIO_SELECTION', null, '0,1', null, null, '0'), + (220, 'proxies', 'COMPOSITE_TABLE', null, 'active,TABLE_ENTRY|autoDiscovery,autoConfiguration,http,https,ftp,socks,rtsp', null, null, null), + (221, 'ExcludeSimpleHostnames', 'CHECKBOX', 220, null, null, 'showInView=true,createDefaultValue=true', 'false'), + (222, 'ExceptionsList', 'TEXT_AREA', 220, null, null, 'showInView=true,createDefaultValue=true', null), + (223, 'FTPPassive', 'CHECKBOX', 220, null, null, 'showInView=true,createDefaultValue=true', 'true'), + (231, 'AutoDiscoveryEnabled', 'CHECKBOX', 220, null, null, 'groupId=autoDiscovery,createDefaultValue=true', 'false'), + (233, 'AutoConfigurationEnabled', 'CHECKBOX', 220, null, null, 'groupId=autoConfiguration,createDefaultValue=true', 'false'), + (234, 'AutoConfigurationURL', 'TEXT_FIELD', 220, null, null, 'groupId=autoConfiguration,createDefaultValue=true', null), + (235, 'AutoConfigurationJavaScript', 'TEXT_AREA', 220, null, null, 'groupId=autoConfiguration,createDefaultValue=true', null), + (236, 'HTTPEnable', 'CHECKBOX', 220, null, null, 'groupId=http,createDefaultValue=true', 'false'), + (237, 'HTTPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=http,createDefaultValue=true', null), + (238, 'HTTPPort', 'INTEGER', 220, null, null, 'groupId=http,createDefaultValue=true', '80'), + (239, 'HTTPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=http,createDefaultValue=true', 'false'), + (240, 'HTTPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=http,createDefaultValue=true', null), + (241, 'HTTPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=http,createDefaultValue=true', null), + (242, 'HTTPSEnable', 'CHECKBOX', 220, null, null, 'groupId=https,createDefaultValue=true', 'false'), + (243, 'HTTPSProxy', 'TEXT_FIELD', 220, null, null, 'groupId=https,createDefaultValue=true', null), + (244, 'HTTPSPort', 'INTEGER', 220, null, null, 'groupId=https,createDefaultValue=true', '443'), + (245, 'HTTPSRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=https,createDefaultValue=true', 'false'), + (246, 'HTTPSUsername', 'TEXT_FIELD', 220, null, null, 'groupId=https,createDefaultValue=true', null), + (247, 'HTTPSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=https,createDefaultValue=true', null), + (248, 'FTPEnable', 'CHECKBOX', 220, null, null, 'groupId=ftp,createDefaultValue=true', 'false'), + (249, 'FTPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=ftp,createDefaultValue=true', null), + (250, 'FTPPort', 'INTEGER', 220, null, null, 'groupId=ftp,createDefaultValue=true', '21'), + (251, 'FTPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=ftp,createDefaultValue=true', 'false'), + (252, 'FTPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=ftp,createDefaultValue=true', null), + (253, 'FTPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=ftp,createDefaultValue=true', null), + (254, 'SOCKSEnable', 'CHECKBOX', 220, null, null, 'groupId=socks,createDefaultValue=true', 'false'), + (255, 'SOCKSProxy', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null), + (256, 'SOCKSPort', 'INTEGER', 220, null, null, 'groupId=socks,createDefaultValue=true', '1080'), + (257, 'SOCKSRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=socks,createDefaultValue=true', 'false'), + (258, 'SOCKSUsername', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null), + (259, 'SOCKSPassword', 'TEXT_FIELD', 220, null, null, 'groupId=socks,createDefaultValue=true', null), + (260, 'RTSPEnable', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'), + (261, 'RTSPProxy', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), + (262, 'RTSPPort', 'INTEGER', 220, null, null, 'groupId=rtsp,createDefaultValue=true', '1080'), + (263, 'RTSPRequiresPassword', 'CHECKBOX', 220, null, null, 'groupId=rtsp,createDefaultValue=true', 'false'), + (264, 'RTSPUsername', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), + (265, 'RTSPPassword', 'TEXT_FIELD', 220, null, null, 'groupId=rtsp,createDefaultValue=true', null), + + + (300, 'sebServicePolicy', 'RADIO_SELECTION', null, '0,1,2', null, null, '2'), + (301, 'kioskMode', 'RADIO_SELECTION', null, '0,1,2', null, null, '0'), + (302, 'allowVirtualMachine', 'CHECKBOX', null, null, null, null, 'false'), + (303, 'allowScreenSharing', 'CHECKBOX', null, null, null, null, 'false'), + (304, 'enablePrivateClipboard', 'CHECKBOX', null, null, null, null, 'true'), + (305, 'enableLogging', 'CHECKBOX', null, null, null, null, 'false'), + (306, 'logDirectoryWin', 'TEXT_FIELD', null, null, null, null, ''), + (307, 'logDirectoryOSX', 'TEXT_FIELD', null, null, null, null, '~/Documents'), + (308, 'minMacOSVersion', 'SINGLE_SELECTION', null, '0,1,2,3,4,5,6,7', null, null, '0'), + (309, 'enableAppSwitcherCheck', 'CHECKBOX', null, null, null, null, 'true'), + (310, 'forceAppFolderInstall', 'CHECKBOX', null, null, null, null, 'true'), + (311, 'allowUserAppFolderInstall', 'CHECKBOX', null, null, null, null, 'false'), + (312, 'allowSiri', 'CHECKBOX', null, null, null, null, 'false'), + (313, 'detectStoppedProcess', 'CHECKBOX', null, null, null, null, 'true'), + (314, 'allowDisplayMirroring', 'CHECKBOX', null, null, null, null, 'false'), + (315, 'allowedDisplaysMaxNumber', 'COMBO_SELECTION', null, '1,2,3', null, null, '1'), + (316, 'allowedDisplayBuiltin', 'CHECKBOX', null, null, null, null, 'true'), + + (400, 'insideSebEnableSwitchUser', 'CHECKBOX', null, null, null, null, 'false'), + (401, 'insideSebEnableLockThisComputer', 'CHECKBOX', null, null, null, null, 'false'), + (402, 'insideSebEnableChangeAPassword', 'CHECKBOX', null, null, null, null, 'false'), + (403, 'insideSebEnableStartTaskManager', 'CHECKBOX', null, null, null, null, 'false'), + (404, 'insideSebEnableLogOff', 'CHECKBOX', null, null, null, null, 'false'), + (405, 'insideSebEnableShutDown', 'CHECKBOX', null, null, null, null, 'false'), + (406, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'), + (407, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'), + (408, 'insideSebEnableNetworkConnectionSelector', 'CHECKBOX', null, null, null, null, 'false'), + + (500, 'enableEsc', 'CHECKBOX', null, null, null, null, 'false'), + (501, 'enablePrintScreen', 'CHECKBOX', null, null, null, null, 'false'), + (502, 'enableCtrlEsc', 'CHECKBOX', null, null, null, null, 'false'), + (503, 'enableAltEsc', 'CHECKBOX', null, null, null, null, 'false'), + (504, 'enableAltTab', 'CHECKBOX', null, null, null, null, 'true'), + (505, 'enableAltF4', 'CHECKBOX', null, null, null, null, 'false'), + (506, 'enableStartMenu', 'CHECKBOX', null, null, null, null, 'false'), + (507, 'enableRightMouse', 'CHECKBOX', null, null, null, null, 'false'), + (508, 'enableAltMouseWheel', 'CHECKBOX', null, null, null, null, 'false'), + + (509, 'enableF1', 'CHECKBOX', null, null, null, null, 'false'), + (510, 'enableF2', 'CHECKBOX', null, null, null, null, 'false'), + (511, 'enableF3', 'CHECKBOX', null, null, null, null, 'false'), + (512, 'enableF4', 'CHECKBOX', null, null, null, null, 'false'), + (513, 'enableF5', 'CHECKBOX', null, null, null, null, 'false'), + (514, 'enableF6', 'CHECKBOX', null, null, null, null, 'false'), + (515, 'enableF7', 'CHECKBOX', null, null, null, null, 'false'), + (516, 'enableF8', 'CHECKBOX', null, null, null, null, 'false'), + (517, 'enableF9', 'CHECKBOX', null, null, null, null, 'false'), + (518, 'enableF10', 'CHECKBOX', null, null, null, null, 'false'), + (519, 'enableF11', 'CHECKBOX', null, null, null, null, 'false'), + (520, 'enableF12', 'CHECKBOX', null, null, null, null, 'false'), + + (1000, 'originatorVersion', 'TEXT_FIELD', null, null, null, null, 'SEB_Server_0.3.0'), + (1001, 'sebConfigPurpose', 'RADIO_SELECTION', null, '0,1', null, null, '0') + + ; +INSERT IGNORE INTO orientation VALUES + (1, 1, 0, 1, null, 1, 1, 1, 2, 'LEFT'), + (2, 2, 0, 1, null, 1, 3, 1, 1, 'LEFT'), + (3, 3, 0, 1, null, 1, 4, 1, 1, 'LEFT'), + (4, 4, 0, 1, null, 1, 5, 1, 2, 'LEFT'), + (5, 5, 0, 1, 'exitSequence', 2, 1, 1, 1, 'NONE'), + (6, 6, 0, 1, 'exitSequence', 2, 2, 1, 1, 'NONE'), + (7, 7, 0, 1, 'exitSequence', 2, 3, 1, 1, 'NONE'), + + (8, 8, 0, 2, 'browserViewMode', 0, 0, 3, 3, 'NONE'), + (9, 9, 0, 2, 'browserViewMode', 3, 2, 4, 1, 'NONE'), + (10, 10, 0, 2, 'winsize', 1, 4, 2, 1, 'LEFT'), + (11, 11, 0, 2, 'winsize', 1, 5, 2, 1, 'LEFT'), + (12, 12, 0, 2, 'winsize', 5, 4, 2, 1, 'LEFT_SPAN'), + (13, 13, 0, 2, 'wintoolbar', 0, 6, 3, 1, 'NONE'), + (14, 14, 0, 2, 'wintoolbar', 3, 6, 4, 1, 'NONE'), + (15, 15, 0, 2, 'wintoolbar', 0, 7, 3, 1, 'NONE'), + (16, 16, 0, 2, 'taskbar', 0, 9, 3, 1, 'NONE'), + (17, 17, 0, 2, 'taskbar', 5, 9, 2, 1, 'LEFT_SPAN'), + (18, 18, 0, 2, 'taskbar', 0, 10, 3, 1, 'NONE'), + (19, 19, 0, 2, 'taskbar', 0, 11, 3, 1, 'NONE'), + (20, 20, 0, 2, 'taskbar', 0, 12, 3, 1, 'NONE'), + (21, 21, 0, 2, 'zoom', 0, 14, 3, 1, 'NONE'), + (22, 22, 0, 2, 'zoom', 0, 15, 3, 1, 'NONE'), + (23, 23, 0, 2, 'zoomMode', 3, 14, 4, 1, 'NONE'), + (24, 24, 0, 2, 'audio', 7, 0, 5, 1, 'NONE'), + (25, 25, 0, 2, 'audio', 7, 1, 5, 1, 'NONE'), + (26, 26, 0, 2, 'audio', 7, 2, 5, 1, 'NONE'), + (27, 27, 0, 2, 'audio', 7, 3, 5, 1, 'NONE'), + (28, 28, 0, 2, 'spellcheck', 7, 4, 5, 1, 'NONE'), + (29, 29, 0, 2, 'spellcheck', 7, 5, 5, 1, 'NONE'), + (30, 30, 0, 2, 'spellcheck', 7, 7, 5, 9, 'TOP'), + + (31, 31, 0, 3, 'newBrowserWindow', 0, 0, 3, 3, 'NONE'), + (32, 32, 0, 3, 'newBrowserWindow', 4, 0, 3, 1, 'NONE'), + (33, 33, 0, 3, 'newwinsize', 1, 4, 2, 1, 'LEFT'), + (34, 34, 0, 3, 'newwinsize', 1, 5, 2, 1, 'LEFT'), + (35, 35, 0, 3, 'newwinsize', 5, 4, 2, 1, 'LEFT_SPAN'), + (36, 36, 0, 3, 'browserSecurity', 0, 5, 4, 1, 'NONE'), + (37, 37, 0, 3, 'browserSecurity', 4, 5, 3, 1, 'NONE'), + (38, 38, 0, 3, 'browserSecurity', 0, 6, 4, 1, 'NONE'), + (39, 39, 0, 3, 'browserSecurity', 4, 6, 3, 1, 'NONE'), + (40, 40, 0, 3, 'browserSecurity', 0, 7, 4, 1, 'NONE'), + (41, 41, 0, 3, 'browserSecurity', 4, 7, 3, 1, 'NONE'), + (42, 42, 0, 3, 'browserSecurity', 0, 8, 4, 1, 'NONE'), + (43, 43, 0, 3, 'browserSecurity', 4, 8, 3, 1, 'NONE'), + (44, 44, 0, 3, 'browserSecurity', 0, 9, 4, 1, 'NONE'), + (45, 45, 0, 3, 'browserSecurity', 4, 9, 3, 1, 'NONE'), + (46, 46, 0, 3, 'browserSecurity', 0, 10, 4, 1, 'NONE'), + (47, 47, 0, 3, 'browserSecurity', 4, 10, 3, 1, 'NONE'), + (48, 48, 0, 3, 'browserSecurity', 0, 11, 4, 1, 'NONE'), + (49, 49, 0, 3, 'browserSecurity', 4, 11, 3, 1, 'NONE'), + + (50, 50, 0, 3, null, 7, 1, 5, 1, 'TOP'), + (51, 51, 0, 3, 'userAgentDesktop', 7, 2, 5, 2, 'NONE'), + (52, 52, 0, 3, 'userAgentDesktop', 7, 3, 5, 1, 'NONE'), + (53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 3, 'NONE'), + (54, 54, 0, 3, 'userAgentTouch', 7, 8, 5, 1, 'NONE'), + (55, 55, 0, 3, 'userAgentMac', 7, 9, 5, 2, 'NONE'), + (56, 56, 0, 3, 'userAgentMac', 7, 11, 5, 1, 'NONE'), + (57, 57, 0, 3, null, 0, 14, 6, 1, 'NONE'), + (58, 58, 0, 3, null, 7, 14, 5, 1, 'TOP'), + + (59, 59, 0, 4, null, 0, 0, 8, 1, 'NONE'), + (60, 60, 0, 4, null, 3, 1, 5, 1, 'LEFT_SPAN'), + (61, 61, 0, 4, null, 3, 2, 5, 1, 'LEFT_SPAN'), + (62, 62, 0, 4, null, 0, 3, 8, 1, 'NONE'), + (63, 63, 0, 4, null, 0, 5, 8, 2, 'TOP'), + (64, 64, 0, 4, null, 0, 8, 8, 1, 'NONE'), + (65, 65, 0, 4, null, 0, 9, 8, 1, 'NONE'), + (66, 66, 0, 4, null, 0, 10, 8, 1, 'NONE'), + + (67, 67, 0, 5, 'quitLink', 0, 1, 8, 1, 'TOP'), + (68, 68, 0, 5, 'quitLink', 0, 2, 8, 1, 'NONE'), + (69, 69, 0, 5, 'backToStart', 0, 4, 8, 1, 'NONE'), + (70, 70, 0, 5, 'backToStart', 0, 6, 8, 2, 'TOP'), + (71, 71, 0, 5, 'backToStart', 0, 8, 8, 2, 'TOP'), + (72, 72, 0, 5, 'backToStart', 0, 10, 8, 1, 'NONE'), + + (73, 73, 0, 6, null, 0, 2, 10, 6, 'TOP'), + (74, 74, 0, 6, null, 1, 1, 1, 1, 'LEFT'), + (75, 75, 0, 6, null, 2, 2, 1, 1, 'LEFT'), + (76, 76, 0, 6, null, 4, 4, 2, 1, 'LEFT'), + (77, 77, 0, 6, null, 0, 3, 1, 1, 'LEFT'), + (78, 78, 0, 6, null, 3, 4, 4, 1, 'LEFT'), + (79, 79, 0, 6, null, 0, 5, 1, 1, 'LEFT'), + (80, 80, 0, 6, null, 0, 6, 1, 1, 'LEFT'), + (81, 81, 0, 6, null, 0, 7, 1, 1, 'LEFT'), + + (82, 82, 0, 6, null, 0, 8, 1, 3, 'LEFT'), + + (85, 85, 0, 6, null, 0, 8, 1, 1, 'LEFT'), + (86, 86, 0, 6, null, 0, 7, 1, 1, 'LEFT'), + (87, 87, 0, 6, null, 0, 9, 1, 1, 'LEFT'), + (88, 88, 0, 6, null, 0, 10, 1, 1, 'LEFT'), + (89, 89, 0, 6, null, 0, 11, 1, 1, 'LEFT'), + (90, 90, 0, 6, null, 0, 12, 1, 1, 'LEFT'), + (91, 91, 0, 6, null, 0, 0, 5, 1, 'NONE'), + (92, 92, 0, 6, null, 5, 0, 5, 1, 'NONE'), + (93, 93, 0, 6, null, 0, 10, 10, 6, 'TOP'), + (94, 94, 0, 6, null, 1, 1, 1, 1, 'LEFT'), + (95, 95, 0, 6, null, 2, 2, 1, 1, 'LEFT'), + (96, 96, 0, 6, null, 3, 3, 4, 1, 'LEFT'), + (97, 97, 0, 6, null, 4, 5, 2, 1, 'LEFT'), + (98, 98, 0, 6, null, 0, 4, 1, 1, 'LEFT'), + (99, 99, 0, 6, null, 0, 6, 1, 1, 'LEFT'), + (100, 100, 0, 6, null, 0, 7, 1, 1, 'LEFT'), + + (200, 200, 0, 8, 'urlFilter', 0, 0, 3, 1, 'NONE'), + (201, 201, 0, 8, 'urlFilter', 3, 0, 4, 1, 'NONE'), + (202, 202, 0, 8, 'urlFilter', 0, 1, 12, 6, 'NONE'), + (203, 203, 0, 8, 'urlFilter', 1, 1, 1, 1, 'LEFT'), + (204, 204, 0, 8, 'urlFilter', 2, 2, 1, 1, 'LEFT'), + (205, 205, 0, 8, 'urlFilter', 3, 3, 4, 1, 'LEFT'), + (206, 206, 0, 8, 'urlFilter', 4, 4, 2, 1, 'LEFT'), + + (210, 210, 0, 8, 'proxies', 0, 6, 5, 2, 'NONE'), + (220, 220, 0, 8, 'proxies', 7, 7, 5, 7, 'TOP'), + (221, 221, 0, 8, 'proxies', 0, 8, 6, 1, 'NONE'), + (222, 222, 0, 8, 'proxies', 0, 10, 6, 2, 'TOP'), + (223, 223, 0, 8, 'proxies', 0, 11, 6, 1, 'NONE'), + + (231, 231, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + + (233, 233, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + (234, 234, 0, 8, null, 0, 1, 1, 1, 'LEFT'), + (235, 235, 0, 8, null, 0, 2, 1, 1, 'LEFT'), + (236, 236, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + (237, 237, 0, 8, null, 0, 1, 1, 1, 'LEFT'), + (238, 238, 0, 8, null, 0, 2, 1, 1, 'LEFT'), + (239, 239, 0, 8, null, 0, 3, 1, 1, 'LEFT'), + (240, 240, 0, 8, null, 0, 4, 1, 1, 'LEFT'), + (241, 241, 0, 8, null, 0, 5, 1, 1, 'LEFT'), + (242, 242, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + (243, 243, 0, 8, null, 0, 1, 1, 1, 'LEFT'), + (244, 244, 0, 8, null, 0, 2, 1, 1, 'LEFT'), + (245, 245, 0, 8, null, 0, 3, 1, 1, 'LEFT'), + (246, 246, 0, 8, null, 0, 4, 1, 1, 'LEFT'), + (247, 247, 0, 8, null, 0, 5, 1, 1, 'LEFT'), + (248, 248, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + (249, 249, 0, 8, null, 0, 1, 1, 1, 'LEFT'), + (250, 250, 0, 8, null, 0, 2, 1, 1, 'LEFT'), + (251, 251, 0, 8, null, 0, 3, 1, 1, 'LEFT'), + (252, 252, 0, 8, null, 0, 4, 1, 1, 'LEFT'), + (253, 253, 0, 8, null, 0, 5, 1, 1, 'LEFT'), + (254, 254, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + (255, 255, 0, 8, null, 0, 1, 1, 1, 'LEFT'), + (256, 256, 0, 8, null, 0, 2, 1, 1, 'LEFT'), + (257, 257, 0, 8, null, 0, 3, 1, 1, 'LEFT'), + (258, 258, 0, 8, null, 0, 4, 1, 1, 'LEFT'), + (259, 259, 0, 8, null, 0, 5, 1, 1, 'LEFT'), + (260, 260, 0, 8, 'active', 0, 0, 1, 1, 'LEFT'), + (261, 261, 0, 8, null, 0, 1, 1, 1, 'LEFT'), + (262, 262, 0, 8, null, 0, 2, 1, 1, 'LEFT'), + (263, 263, 0, 8, null, 0, 3, 1, 1, 'LEFT'), + (264, 264, 0, 8, null, 0, 4, 1, 1, 'LEFT'), + (265, 265, 0, 8, null, 0, 5, 1, 1, 'LEFT'), + + + (300, 300, 0, 9, 'servicePolicy', 0, 0, 4, 3, 'NONE'), + (301, 301, 0, 9, 'kioskMode', 4, 0, 3, 3, 'NONE'), + (302, 302, 0, 9, null, 0, 5, 4, 1, 'NONE'), + (303, 303, 0, 9, null, 0, 6, 4, 1, 'NONE'), + (304, 304, 0, 9, null, 4, 5, 3, 1, 'NONE'), + (305, 305, 0, 9, 'logging', 0, 8, 6, 1, 'NONE'), + (306, 306, 0, 9, 'logging', 3, 9, 4, 1, 'LEFT_SPAN'), + (307, 307, 0, 9, 'logging', 3, 10, 4, 1, 'LEFT_SPAN'), + (308, 308, 0, 9, 'macSettings', 7, 1, 5, 1, 'TOP'), + (309, 309, 0, 9, 'macSettings', 7, 2, 5, 1, 'NONE'), + (310, 310, 0, 9, 'macSettings', 7, 3, 5, 1, 'NONE'), + (311, 311, 0, 9, 'macSettings', 7, 4, 5, 1, 'NONE'), + (312, 312, 0, 9, 'macSettings', 7, 5, 5, 1, 'NONE'), + (313, 313, 0, 9, 'macSettings', 7, 6, 5, 1, 'NONE'), + (314, 314, 0, 9, 'macSettings', 7, 7, 5, 1, 'NONE'), + (315, 315, 0, 9, 'macSettings', 7, 9, 5, 1, 'TOP'), + (316, 316, 0, 9, 'macSettings', 7, 10, 5, 1, 'NONE'), + + (400, 400, 0, 10, 'registry', 0, 1, 4, 1, 'NONE'), + (401, 401, 0, 10, 'registry', 0, 2, 4, 1, 'NONE'), + (402, 402, 0, 10, 'registry', 0, 3, 4, 1, 'NONE'), + (403, 403, 0, 10, 'registry', 0, 4, 4, 1, 'NONE'), + (404, 404, 0, 10, 'registry', 0, 5, 4, 1, 'NONE'), + (405, 405, 0, 10, 'registry', 0, 6, 4, 1, 'NONE'), + (406, 406, 0, 10, 'registry', 0, 7, 4, 1, 'NONE'), + (407, 407, 0, 10, 'registry', 0, 8, 4, 1, 'NONE'), + (408, 408, 0, 10, 'registry', 0, 9, 4, 1, 'NONE'), + + (500, 500, 0, 11, 'specialKeys', 0, 1, 3, 1, 'NONE'), + (501, 501, 0, 11, 'specialKeys', 0, 2, 3, 1, 'NONE'), + (502, 502, 0, 11, 'specialKeys', 0, 3, 3, 1, 'NONE'), + (503, 503, 0, 11, 'specialKeys', 0, 4, 3, 1, 'NONE'), + (504, 504, 0, 11, 'specialKeys', 0, 5, 3, 1, 'NONE'), + (505, 505, 0, 11, 'specialKeys', 0, 6, 3, 1, 'NONE'), + (506, 506, 0, 11, 'specialKeys', 0, 7, 3, 1, 'NONE'), + (507, 507, 0, 11, 'specialKeys', 0, 8, 3, 1, 'NONE'), + (508, 508, 0, 11, 'specialKeys', 0, 9, 3, 1, 'NONE'), + + (509, 509, 0, 11, 'functionKeys', 3, 1, 3, 1, 'NONE'), + (510, 510, 0, 11, 'functionKeys', 3, 2, 3, 1, 'NONE'), + (511, 511, 0, 11, 'functionKeys', 3, 3, 3, 1, 'NONE'), + (512, 512, 0, 11, 'functionKeys', 3, 4, 3, 1, 'NONE'), + (513, 513, 0, 11, 'functionKeys', 3, 5, 3, 1, 'NONE'), + (514, 514, 0, 11, 'functionKeys', 3, 6, 3, 1, 'NONE'), + (515, 515, 0, 11, 'functionKeys', 3, 7, 3, 1, 'NONE'), + (516, 516, 0, 11, 'functionKeys', 3, 8, 3, 1, 'NONE'), + (517, 517, 0, 11, 'functionKeys', 3, 9, 3, 1, 'NONE'), + (518, 518, 0, 11, 'functionKeys', 3, 10, 3, 1, 'NONE'), + (519, 519, 0, 11, 'functionKeys', 3, 11, 3, 1, 'NONE'), + (520, 520, 0, 11, 'functionKeys', 3, 12, 3, 1, 'NONE') + + ; + + + + + + +INSERT IGNORE INTO configuration_node VALUES + (1, 1, 0, 'super-admin', 'test', null, 'EXAM_CONFIG', 'READY_TO_USE') + ; + +INSERT IGNORE INTO configuration VALUES + (1, 1, 1, 'v0', '2019-07-02 12:59:32', 0), + (2, 1, 1, null, null, 1) + ; + +INSERT IGNORE INTO configuration_value VALUES + (1,1,1,1,0,NULL), + (2,1,1,2,0,'true'), + (3,1,1,3,0,'false'), + (4,1,1,4,0,NULL), + (5,1,1,5,0,'2'), + (6,1,1,6,0,'10'), + (7,1,1,7,0,'5'), + (8,1,1,8,0,'0'), + (9,1,1,9,0,'false'), + (10,1,1,10,0,'100%'), + (11,1,1,11,0,'100%'), + (12,1,1,12,0,'1'), + (13,1,1,13,0,'false'), + (14,1,1,14,0,'false'), + (15,1,1,15,0,'false'), + (16,1,1,16,0,'true'), + (17,1,1,17,0,'40'), + (18,1,1,18,0,'true'), + (19,1,1,19,0,'true'), + (20,1,1,20,0,'false'), + (21,1,1,21,0,'true'), + (22,1,1,22,0,'true'), + (23,1,1,23,0,'0'), + (24,1,1,24,0,'false'), + (25,1,1,25,0,'false'), + (26,1,1,26,0,'false'), + (27,1,1,27,0,'25'), + (28,1,1,28,0,'false'), + (29,1,1,29,0,'false'), + (30,1,1,30,0,'da-DK,en-AU,en-GB,en-US,es-ES,fr-FR,pt-PT,sv-SE,sv-FI'), + (31,1,1,31,0,'2'), + (32,1,1,32,0,'false'), + (33,1,1,33,0,'100%'), + (34,1,1,34,0,'100%'), + (35,1,1,35,0,'2'), + (36,1,1,36,0,'true'), + (37,1,1,37,0,'true'), + (38,1,1,38,0,'false'), + (39,1,1,39,0,'false'), + (40,1,1,40,0,'false'), + (41,1,1,41,0,'false'), + (42,1,1,42,0,'false'), + (43,1,1,43,0,'true'), + (44,1,1,44,0,'true'), + (45,1,1,45,0,'true'), + (46,1,1,46,0,'true'), + (47,1,1,47,0,'false'), + (48,1,1,48,0,'false'), + (49,1,1,49,0,'false'), + (50,1,1,50,0,NULL), + (51,1,1,51,0,'0'), + (52,1,1,52,0,NULL), + (53,1,1,53,0,'0'), + (54,1,1,54,0,NULL), + (55,1,1,55,0,'0'), + (56,1,1,56,0,NULL), + (57,1,1,57,0,'true'), + (58,1,1,58,0,NULL), + (59,1,1,59,0,'true'), + (60,1,1,60,0,NULL), + (61,1,1,61,0,NULL), + (62,1,1,62,0,'false'), + (63,1,1,63,0,'0'), + (64,1,1,64,0,'true'), + (65,1,1,65,0,'true'), + (66,1,1,66,0,'true'), + (67,1,1,67,0,NULL), + (68,1,1,68,0,'true'), + (69,1,1,69,0,'false'), + (70,1,1,70,0,NULL), + (71,1,1,71,0,NULL), + (72,1,1,72,0,'true'), + (73,1,1,73,0,NULL), + (74,1,1,91,0,'false'), + (75,1,1,92,0,'false'), + (76,1,1,93,0,NULL), + (77,1,1,200,0,'false'), + (78,1,1,201,0,'false'), + (79,1,1,202,0,NULL), + (80,1,1,210,0,'0'), + (81,1,1,220,0,NULL), + (82,1,1,221,0,'false'), + (83,1,1,222,0,NULL), + (84,1,1,223,0,'true'), + (85,1,1,231,0,'false'), + (86,1,1,233,0,'false'), + (87,1,1,234,0,NULL), + (88,1,1,235,0,NULL), + (89,1,1,236,0,'false'), + (90,1,1,237,0,NULL), + (91,1,1,238,0,'80'), + (92,1,1,239,0,'false'), + (93,1,1,240,0,NULL), + (94,1,1,241,0,NULL), + (95,1,1,242,0,'false'), + (96,1,1,243,0,NULL), + (97,1,1,244,0,'443'), + (98,1,1,245,0,'false'), + (99,1,1,246,0,NULL), + (100,1,1,247,0,NULL), + (101,1,1,248,0,'false'), + (102,1,1,249,0,NULL), + (103,1,1,250,0,'21'), + (104,1,1,251,0,'false'), + (105,1,1,252,0,NULL), + (106,1,1,253,0,NULL), + (107,1,1,254,0,'false'), + (108,1,1,255,0,NULL), + (109,1,1,256,0,'1080'), + (110,1,1,257,0,'false'), + (111,1,1,258,0,NULL), + (112,1,1,259,0,NULL), + (113,1,1,260,0,'false'), + (114,1,1,261,0,NULL), + (115,1,1,262,0,'1080'), + (116,1,1,263,0,'false'), + (117,1,1,264,0,NULL), + (118,1,1,265,0,NULL), + (119,1,1,300,0,'2'), + (120,1,1,301,0,'0'), + (121,1,1,302,0,'false'), + (122,1,1,303,0,'false'), + (123,1,1,304,0,'true'), + (124,1,1,305,0,'false'), + (125,1,1,306,0,''), + (126,1,1,307,0,'~/Documents'), + (127,1,1,308,0,'0'), + (128,1,1,309,0,'true'), + (129,1,1,310,0,'true'), + (130,1,1,311,0,'false'), + (131,1,1,312,0,'false'), + (132,1,1,313,0,'true'), + (133,1,1,314,0,'false'), + (134,1,1,315,0,'1'), + (135,1,1,316,0,'true'), + (136,1,1,400,0,'false'), + (137,1,1,401,0,'false'), + (138,1,1,402,0,'false'), + (139,1,1,403,0,'false'), + (140,1,1,404,0,'false'), + (141,1,1,405,0,'false'), + (142,1,1,406,0,'false'), + (143,1,1,407,0,'false'), + (144,1,1,408,0,'false'), + (145,1,1,500,0,'false'), + (146,1,1,501,0,'false'), + (147,1,1,502,0,'false'), + (148,1,1,503,0,'false'), + (149,1,1,504,0,'true'), + (150,1,1,505,0,'false'), + (151,1,1,506,0,'false'), + (152,1,1,507,0,'false'), + (153,1,1,508,0,'false'), + (154,1,1,509,0,'false'), + (155,1,1,510,0,'false'), + (156,1,1,511,0,'false'), + (157,1,1,512,0,'false'), + (158,1,1,513,0,'false'), + (159,1,1,514,0,'false'), + (160,1,1,515,0,'false'), + (161,1,1,516,0,'false'), + (162,1,1,517,0,'false'), + (163,1,1,518,0,'false'), + (164,1,1,519,0,'false'), + (165,1,1,520,0,'false'), + (166,1,1,1000,0,'SEB_Server_0.3.0'), + (167,1,1,1001,0,'0'), + (168,1,2,1,0,NULL), + (169,1,2,2,0,'true'), + (170,1,2,3,0,'false'), + (171,1,2,4,0,NULL), + (172,1,2,5,0,'2'), + (173,1,2,6,0,'10'), + (174,1,2,7,0,'5'), + (175,1,2,8,0,'0'), + (176,1,2,9,0,'false'), + (177,1,2,10,0,'100%'), + (178,1,2,11,0,'100%'), + (179,1,2,12,0,'1'), + (180,1,2,13,0,'false'), + (181,1,2,14,0,'false'), + (182,1,2,15,0,'false'), + (183,1,2,16,0,'true'), + (184,1,2,17,0,'40'), + (185,1,2,18,0,'true'), + (186,1,2,19,0,'true'), + (187,1,2,20,0,'false'), + (188,1,2,21,0,'true'), + (189,1,2,22,0,'true'), + (190,1,2,23,0,'0'), + (191,1,2,24,0,'false'), + (192,1,2,25,0,'false'), + (193,1,2,26,0,'false'), + (194,1,2,27,0,'25'), + (195,1,2,28,0,'false'), + (196,1,2,29,0,'false'), + (197,1,2,30,0,'da-DK,en-AU,en-GB,en-US,es-ES,fr-FR,pt-PT,sv-SE,sv-FI'), + (198,1,2,31,0,'2'), + (199,1,2,32,0,'false'), + (200,1,2,33,0,'100%'), + (201,1,2,34,0,'100%'), + (202,1,2,35,0,'2'), + (203,1,2,36,0,'true'), + (204,1,2,37,0,'true'), + (205,1,2,38,0,'false'), + (206,1,2,39,0,'false'), + (207,1,2,40,0,'false'), + (208,1,2,41,0,'false'), + (209,1,2,42,0,'false'), + (210,1,2,43,0,'true'), + (211,1,2,44,0,'true'), + (212,1,2,45,0,'true'), + (213,1,2,46,0,'true'), + (214,1,2,47,0,'false'), + (215,1,2,48,0,'false'), + (216,1,2,49,0,'false'), + (217,1,2,50,0,NULL), + (218,1,2,51,0,'0'), + (219,1,2,52,0,NULL), + (220,1,2,53,0,'0'), + (221,1,2,54,0,NULL), + (222,1,2,55,0,'0'), + (223,1,2,56,0,NULL), + (224,1,2,57,0,'true'), + (225,1,2,58,0,NULL), + (226,1,2,59,0,'true'), + (227,1,2,60,0,NULL), + (228,1,2,61,0,NULL), + (229,1,2,62,0,'false'), + (230,1,2,63,0,'0'), + (231,1,2,64,0,'true'), + (232,1,2,65,0,'true'), + (233,1,2,66,0,'true'), + (234,1,2,67,0,NULL), + (235,1,2,68,0,'true'), + (236,1,2,69,0,'false'), + (237,1,2,70,0,NULL), + (238,1,2,71,0,NULL), + (239,1,2,72,0,'true'), + (240,1,2,73,0,NULL), + (241,1,2,91,0,'false'), + (242,1,2,92,0,'false'), + (243,1,2,93,0,NULL), + (244,1,2,200,0,'false'), + (245,1,2,201,0,'false'), + (246,1,2,202,0,NULL), + (247,1,2,210,0,'0'), + (248,1,2,220,0,NULL), + (249,1,2,221,0,'false'), + (250,1,2,222,0,NULL), + (251,1,2,223,0,'true'), + (252,1,2,231,0,'false'), + (253,1,2,233,0,'false'), + (254,1,2,234,0,NULL), + (255,1,2,235,0,NULL), + (256,1,2,236,0,'false'), + (257,1,2,237,0,NULL), + (258,1,2,238,0,'80'), + (259,1,2,239,0,'false'), + (260,1,2,240,0,NULL), + (261,1,2,241,0,NULL), + (262,1,2,242,0,'false'), + (263,1,2,243,0,NULL), + (264,1,2,244,0,'443'), + (265,1,2,245,0,'false'), + (266,1,2,246,0,NULL), + (267,1,2,247,0,NULL), + (268,1,2,248,0,'false'), + (269,1,2,249,0,NULL), + (270,1,2,250,0,'21'), + (271,1,2,251,0,'false'), + (272,1,2,252,0,NULL), + (273,1,2,253,0,NULL), + (274,1,2,254,0,'false'), + (275,1,2,255,0,NULL), + (276,1,2,256,0,'1080'), + (277,1,2,257,0,'false'), + (278,1,2,258,0,NULL), + (279,1,2,259,0,NULL), + (280,1,2,260,0,'false'), + (281,1,2,261,0,NULL), + (282,1,2,262,0,'1080'), + (283,1,2,263,0,'false'), + (284,1,2,264,0,NULL), + (285,1,2,265,0,NULL), + (286,1,2,300,0,'2'), + (287,1,2,301,0,'0'), + (288,1,2,302,0,'false'), + (289,1,2,303,0,'false'), + (290,1,2,304,0,'true'), + (291,1,2,305,0,'false'), + (292,1,2,306,0,''), + (293,1,2,307,0,'~/Documents'), + (294,1,2,308,0,'0'), + (295,1,2,309,0,'true'), + (296,1,2,310,0,'true'), + (297,1,2,311,0,'false'), + (298,1,2,312,0,'false'), + (299,1,2,313,0,'true'), + (300,1,2,314,0,'false'), + (301,1,2,315,0,'1'), + (302,1,2,316,0,'true'), + (303,1,2,400,0,'false'), + (304,1,2,401,0,'false'), + (305,1,2,402,0,'false'), + (306,1,2,403,0,'false'), + (307,1,2,404,0,'false'), + (308,1,2,405,0,'false'), + (309,1,2,406,0,'false'), + (310,1,2,407,0,'false'), + (311,1,2,408,0,'false'), + (312,1,2,500,0,'false'), + (313,1,2,501,0,'false'), + (314,1,2,502,0,'false'), + (315,1,2,503,0,'false'), + (316,1,2,504,0,'true'), + (317,1,2,505,0,'false'), + (318,1,2,506,0,'false'), + (319,1,2,507,0,'false'), + (320,1,2,508,0,'false'), + (321,1,2,509,0,'false'), + (322,1,2,510,0,'false'), + (323,1,2,511,0,'false'), + (324,1,2,512,0,'false'), + (325,1,2,513,0,'false'), + (326,1,2,514,0,'false'), + (327,1,2,515,0,'false'), + (328,1,2,516,0,'false'), + (329,1,2,517,0,'false'), + (330,1,2,518,0,'false'), + (331,1,2,519,0,'false'), + (332,1,2,520,0,'false'), + (333,1,2,1000,0,'SEB_Server_0.3.0'), + (334,1,2,1001,0,'0') + ; + +INSERT IGNORE INTO exam_configuration_map VALUES + (1, 1, 2, 1, null, null) + ; + diff --git a/src/test/resources/schema-test.sql b/src/test/resources/schema-test.sql index 0db7d8c0..94a60f3c 100644 --- a/src/test/resources/schema-test.sql +++ b/src/test/resources/schema-test.sql @@ -84,21 +84,26 @@ DROP TABLE IF EXISTS `client_connection` ; CREATE TABLE IF NOT EXISTS `client_connection` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `institution_id` BIGINT UNSIGNED NOT NULL, `exam_id` BIGINT UNSIGNED NULL, `status` VARCHAR(45) NOT NULL, `connection_token` VARCHAR(255) NOT NULL, - `user_name` VARCHAR(255) NOT NULL, - `VDI` BIT(1) NOT NULL, + `exam_user_session_identifer` VARCHAR(255) NULL, `client_address` VARCHAR(45) NOT NULL, `virtual_client_address` VARCHAR(45) NULL, PRIMARY KEY (`id`), INDEX `connection_exam_ref_idx` (`exam_id` ASC), + INDEX `clientConnectionInstitutionRef_idx` (`institution_id` ASC), CONSTRAINT `clientConnectionExamRef` FOREIGN KEY (`exam_id`) REFERENCES `exam` (`id`) ON DELETE NO ACTION - ON UPDATE NO ACTION) -; + ON UPDATE NO ACTION, + CONSTRAINT `clientConnectionInstitutionRef` + FOREIGN KEY (`institution_id`) + REFERENCES `institution` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION); -- -----------------------------------------------------