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 856d9ed6..4016de04 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 @@ -175,7 +175,7 @@ public final class API { public static final String LMS_FULL_INTEGRATION_EXAM_TEMPLATE_ID = "exam_template_id"; public static final String LMS_FULL_INTEGRATION_QUIT_PASSWORD = "quit_password"; public static final String LMS_FULL_INTEGRATION_QUIT_LINK = "quit_link"; - + public static final String LMS_FULL_INTEGRATION_TIME_ZONE = "account_time_zone"; public static final String USER_ACCOUNT_ENDPOINT = "/useraccount"; diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java index e51f19a4..8d41bc1c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/user/UserMod.java @@ -21,6 +21,7 @@ import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -95,6 +96,12 @@ public final class UserMod implements UserAccount { @JsonProperty(PasswordChange.ATTR_NAME_CONFIRM_NEW_PASSWORD) private final CharSequence confirmNewPassword; + @JsonProperty(USER.ATTR_LOCAL_ACCOUNT) + private final Boolean isLocalAccount; + + @JsonProperty(USER.ATTR_DIRECT_LOGIN) + private final Boolean hasDirectLogin; + @JsonCreator public UserMod( @JsonProperty(USER.ATTR_UUID) final String uuid, @@ -107,6 +114,8 @@ public final class UserMod implements UserAccount { @JsonProperty(USER.ATTR_EMAIL) final String email, @JsonProperty(USER.ATTR_LANGUAGE) final Locale language, @JsonProperty(USER.ATTR_TIMEZONE) final DateTimeZone timeZone, + @JsonProperty(USER.ATTR_LOCAL_ACCOUNT) final Boolean isLocalAccount, + @JsonProperty(USER.ATTR_DIRECT_LOGIN) final Boolean hasDirectLogin, @JsonProperty(USER_ROLE.REFERENCE_NAME) final Set roles) { this.uuid = uuid; @@ -119,6 +128,8 @@ public final class UserMod implements UserAccount { this.email = email; this.language = (language != null) ? language : Locale.ENGLISH; this.timeZone = (timeZone != null) ? timeZone : DateTimeZone.UTC; + this.isLocalAccount = BooleanUtils.isNotFalse(isLocalAccount); + this.hasDirectLogin = BooleanUtils.isNotFalse(hasDirectLogin); this.roles = (roles != null) ? Collections.unmodifiableSet(roles) : Collections.emptySet(); @@ -136,6 +147,8 @@ public final class UserMod implements UserAccount { this.language = postAttrMapper.getLocale(USER.ATTR_LANGUAGE); this.timeZone = postAttrMapper.getDateTimeZone(USER.ATTR_TIMEZONE); this.roles = postAttrMapper.getStringSet(USER_ROLE.REFERENCE_NAME); + this.isLocalAccount = BooleanUtils.isNotFalse(postAttrMapper.getBoolean(USER.ATTR_LOCAL_ACCOUNT)); + this.hasDirectLogin = BooleanUtils.isNotFalse(postAttrMapper.getBoolean(USER.ATTR_DIRECT_LOGIN)); } @Override @@ -237,6 +250,15 @@ public final class UserMod implements UserAccount { return false; } + + public Boolean isLocalAccount() { + return isLocalAccount; + } + + public Boolean hasDirectLogin() { + return hasDirectLogin; + } + @JsonIgnore @Override public EntityKey getEntityKey() { @@ -279,7 +301,7 @@ public final class UserMod implements UserAccount { return new UserMod( UUID.randomUUID().toString(), institutionId, - null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, true, true, null); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java b/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java index 4f4dd196..9a0c9303 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/AdminUserInitializer.java @@ -145,6 +145,8 @@ class AdminUserInitializer { null, null, null, + true, + true, new HashSet<>(this.webserviceInfo.isLightSetup() ? UserRole.getLightSetupRoles() : List.of(UserRole.SEB_SERVER_ADMIN.name()) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java index cbfe3a3f..4688f8c3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java @@ -50,6 +50,7 @@ public class WebserviceInfo { "sebserver.webservice.api.exam.endpoint.discovery"; private static final String WEB_SERVICE_EXTERNAL_ADDRESS_ALIAS = "sebserver.webservice.lms.address.alias"; private static final String WEB_SERVICE_CONTEXT_PATH = "server.servlet.context-path"; + public static final String SEBSERVER_WEBSERVICE_AUTOLOGIN_ENDPOINT = "sebserver.webservice.autologin.endpoint"; private final String sebServerVersion; private final String testProperty; @@ -61,6 +62,8 @@ public class WebserviceInfo { private final String discoveryEndpoint; private final String contextPath; + private final String autoLoginEndpoint; + private final boolean isLightSetup; private final String serverURLPrefix; private final boolean isDistributed; @@ -104,6 +107,9 @@ public class WebserviceInfo { this.webserviceUUID = UUID.randomUUID().toString() + Constants.UNDERLINE + this.sebServerVersion; + this.autoLoginEndpoint = environment.getProperty( + SEBSERVER_WEBSERVICE_AUTOLOGIN_ENDPOINT, + "/auto_login"); this.distributedUpdateInterval = environment.getProperty( "sebserver.webservice.distributed.updateInterval", @@ -237,6 +243,10 @@ public class WebserviceInfo { return this.discoveryEndpoint; } + public String getAutoLoginEndpoint() { + return autoLoginEndpoint; + } + public String getDiscoveryEndpointAddress() { return this.serverURLPrefix + this.discoveryEndpoint; } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java index f8ea0067..6c50983c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/EntityDAO.java @@ -113,16 +113,6 @@ public interface EntityDAO { * happened */ Result> delete(Set all); - @Transactional - default Result deleteOne(final Long examId) { - if (examId == null) { - return Result.ofRuntimeError("exam Id has null reference"); - } - return delete( new HashSet<>(Arrays.asList(new EntityKey(examId, EntityType.EXAM)))) - .map(set -> set.iterator().next()) - .onError(TransactionHandler::rollback); - } - /** Get a (unordered) collection of all Entities that matches the given filter criteria. * The possible filter criteria for a specific Entity type is defined by the entity type. *

diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java index 6ebd91cd..9e73bf31 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/UserDAOImpl.java @@ -252,8 +252,8 @@ public class UserDAOImpl implements UserDAO { userMod.language.toLanguageTag(), userMod.timeZone.getID(), BooleanUtils.toInteger(false), - BooleanUtils.toInteger(true), - BooleanUtils.toInteger(true)); + BooleanUtils.toInteger(userMod.hasDirectLogin()), + BooleanUtils.toInteger(userMod.isLocalAccount())); this.userRecordMapper.insert(recordToSave); final Long newUserPK = recordToSave.getId(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java index c4d94759..af9e36ac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java @@ -42,7 +42,8 @@ public interface FullLmsIntegrationService { String quizId, String examTemplateId, String quitPassword, - String quitLink); + String quitLink, + String timezone); Result deleteExam( String lmsUUID, @@ -66,6 +67,9 @@ public interface FullLmsIntegrationService { public final String name; @JsonProperty("url") public final String url; + @JsonProperty("autologin_url") + public final String autoLoginURL; + @JsonProperty("access_token") public final String access_token; @JsonProperty("exam_templates") @@ -76,12 +80,14 @@ public interface FullLmsIntegrationService { @JsonProperty("id") final String id, @JsonProperty("name") final String name, @JsonProperty("url") final String url, + @JsonProperty("autologin_url") final String autoLoginURL, @JsonProperty("access_token") final String access_token, @JsonProperty("exam_templates") final Collection exam_templates) { this.id = id; this.name = name; this.url = url; + this.autoLoginURL = autoLoginURL; this.access_token = access_token; this.exam_templates = Utils.immutableCollectionOf(exam_templates); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java index 8fcc0ec1..68754ed1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java @@ -12,12 +12,15 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; +import java.util.Locale; +import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.gbl.api.API; import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.api.POSTMapper; import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.EntityKey; @@ -26,8 +29,12 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate; import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; +import ch.ethz.seb.sebserver.gbl.model.user.UserInfo; +import ch.ethz.seb.sebserver.gbl.model.user.UserMod; +import ch.ethz.seb.sebserver.gbl.model.user.UserRole; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; @@ -44,6 +51,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigServi import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -62,6 +70,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService private final LmsSetupDAO lmsSetupDAO; private final UserActivityLogDAO userActivityLogDAO; + private final UserDAO userDAO; private final SEBClientConfigDAO sebClientConfigDAO; private final ClientConfigService clientConfigService; private final DeleteExamAction deleteExamAction; @@ -79,6 +88,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService public FullLmsIntegrationServiceImpl( final LmsSetupDAO lmsSetupDAO, final UserActivityLogDAO userActivityLogDAO, + final UserDAO userDAO, final SEBClientConfigDAO sebClientConfigDAO, final ClientConfigService clientConfigService, final DeleteExamAction deleteExamAction, @@ -96,6 +106,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService this.lmsSetupDAO = lmsSetupDAO; this.userActivityLogDAO = userActivityLogDAO; + this.userDAO = userDAO; this.sebClientConfigDAO = sebClientConfigDAO; this.clientConfigService = clientConfigService; this.deleteExamAction = deleteExamAction; @@ -126,6 +137,11 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService .add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8)); } + @Override + public void notifyExamDeletion(final ExamDeletionEvent event) { + event.ids.forEach(this::deleteAdHocAccount); + } + @Override public void notifyLmsSetupChange(final LmsSetupChangeEvent event) { final LmsSetup lmsSetup = event.getLmsSetup(); @@ -187,6 +203,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService connectionId, lmsSetup.name, getAPIRootURL(), + getAutoLoginURL(), accessToken, this.getIntegrationTemplates(lmsSetup.institutionId) ); @@ -238,13 +255,14 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService final String quizId, final String examTemplateId, final String quitPassword, - final String quitLink) { + final String quitLink, + final String timezone) { return lmsSetupDAO .getLmsSetupIdByConnectionId(lmsUUID) .flatMap(lmsAPIService::getLmsAPITemplate) .map(findQuizData(courseId, quizId)) - .map(createAccountAndExam(examTemplateId, quitPassword)); + .map(createAccountAndExam(examTemplateId, quitPassword, timezone)); } @Override @@ -260,6 +278,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService .flatMap(this::findExam) .map(this::checkDeletion) .map(this::logExamDeleted) + .map(this::deleteAdHocAccount) .flatMap(deleteExamAction::deleteExamFromLMSIntegration); } @@ -276,10 +295,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService return exam; } - @Override - public void notifyExamDeletion(final ExamDeletionEvent event) { - event.ids.forEach(this::deleteAdHocAccount); - } + @Override public Result streamConnectionConfiguration( @@ -306,12 +322,15 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService if (StringUtils.isBlank(connectionConfigId)) { connectionConfigId = this.sebClientConfigDAO .all(exam.institutionId, true) - .map(all -> all.iterator().next()) + .map(all -> all.stream().filter(config -> config.configPurpose == SEBClientConfig.ConfigPurpose.START_EXAM) + .findFirst() + .orElseThrow(() -> new APIMessage.APIMessageException( + APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("No active Connection Configuration found")))) .map(SEBClientConfig::getModelId) .getOr(null); } if (StringUtils.isBlank(connectionConfigId)) { - return Result.ofRuntimeError("No active Connection Configuration found"); + throw new APIMessage.APIMessageException(APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("No active Connection Configuration found")); } this.clientConfigService.exportSEBClientConfiguration(out, connectionConfigId, exam.id); @@ -358,7 +377,8 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService private Function createAccountAndExam( final String examTemplateId, - final String quitPassword) { + final String quitPassword, + final String timezone) { return quizData -> { @@ -378,8 +398,13 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService post.putIfAbsent(Domain.EXAM.ATTR_QUIT_PASSWORD, quitPassword); } - final String accountUUID = createAdHocSupporterAccount(quizData); - post.putIfAbsent(Domain.EXAM.ATTR_OWNER, accountUUID); + final String accountUUID = createAdHocSupporterAccount(quizData, timezone); + if (accountUUID != null) { + post.putIfAbsent(Domain.EXAM.ATTR_OWNER, accountUUID); + // TODO do we need to apply the ad-hoc teacher account also as supporter? + } else { + post.putIfAbsent(Domain.EXAM.ATTR_OWNER, userService.getCurrentUser().uuid()); + } final Exam exam = new Exam(null, quizData, post); return examDAO @@ -390,16 +415,81 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService }; } - private String createAdHocSupporterAccount(final QuizData data) { - // TODO create an ad hoc supporter account for this exam and apply it to the exam - return "mockAccountUUID"; + private String createAdHocSupporterAccount(final QuizData data, final String timezone) { + try { + + final String uuid = UUID.randomUUID().toString(); + final String name = "teacher-" + uuid; + DateTimeZone dtz = DateTimeZone.UTC; + if (StringUtils.isNotBlank(timezone)) { + try { + dtz = DateTimeZone.forID(timezone); + } catch (final Exception e) { + log.warn("Failed to set requested time zone for ad-hoc teacher account: {}", timezone); + } + } + + final UserMod adHocTeacherUser = new UserMod( + uuid, + data.institutionId, + name, + data.id, + name, + uuid, + uuid, + null, + Locale.ENGLISH, + dtz, + true, + false, + Utils.immutableSetOf(UserRole.TEACHER.name())); + + userDAO.createNew(adHocTeacherUser) + .flatMap(account -> userDAO.setActive(account, true)) + .getOrThrow(); + + return uuid; + } catch (final Exception e) { + log.error("Failed to create ad-hoc teacher account for importing exam: {}", data, e); + return null; + } + } + + private Exam deleteAdHocAccount(final Exam exam) { + deleteAdHocAccount(exam.id); + return exam; } private void deleteAdHocAccount(final Long examId) { try { - // TODO check if exam has an ad-hoc account and if true, delete it + + final Result examResult = examDAO.byPK(examId); + if (examResult.hasError()) { + log.warn("Failed to get exam for id: {}", examId); + return; + } + + final String externalId = examResult.get().externalId; + final FilterMap filter = new FilterMap(); + filter.putIfAbsent(Domain.USER.ATTR_SURNAME, externalId); + final Collection accounts = userDAO.allMatching(filter).getOrThrow(); + + if (accounts.isEmpty()) { + return; + } + + if (accounts.size() > 1) { + log.error("Too many accounts found!?... ad-hoc teacher account mapping: {}", externalId); + return; + } + + userDAO.delete(Utils.immutableSetOf(new EntityKey( + accounts.iterator().next().uuid, + EntityType.USER))) + .getOrThrow(); + } catch (final Exception e) { - log.error("Failed to delete ad hoc account for exam: {}", examId, e); + log.error("Failed to delete ad-hoc account for exam: {}", examId, e); } } @@ -420,6 +510,10 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService return webserviceInfo.getExternalServerURL() + lmsAPIEndpoint; } + private String getAutoLoginURL() { + return webserviceInfo.getExternalServerURL() + webserviceInfo.getAutoLoginEndpoint(); + } + private Exam logExamCreated(final Exam exam) { this.userActivityLogDAO .logCreate(exam) 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 83328640..190fd06a 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 @@ -637,8 +637,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic public void streamLightExamConfig(final String modelId, final HttpServletResponse response) throws IOException{ final ServletOutputStream outputStream = response.getOutputStream(); - PipedOutputStream pout; - PipedInputStream pin; + PipedOutputStream pout = null; + PipedInputStream pin= null; try { pout = new PipedOutputStream(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java index 28ab9505..e4144dd3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java @@ -670,6 +670,8 @@ class ScreenProctoringAPIBinding { userInfo.email, userInfo.language, userInfo.timeZone, + true, + true, spsUserRoles); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java index 4c4a2d6d..c7155d6e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java @@ -8,18 +8,23 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import ch.ethz.seb.sebserver.gbl.api.API; +import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -53,9 +58,17 @@ public class LmsIntegrationController { @RequestParam(name = API.LMS_FULL_INTEGRATION_EXAM_TEMPLATE_ID, required = true) final String templateId, @RequestParam(name = API.LMS_FULL_INTEGRATION_QUIT_PASSWORD, required = false) final String quitPassword, @RequestParam(name = API.LMS_FULL_INTEGRATION_QUIT_LINK, required = false) final String quitLink, + @RequestParam(name = API.LMS_FULL_INTEGRATION_TIME_ZONE, required = false) final String timezone, final HttpServletResponse response) { - final Exam exam = fullLmsIntegrationService.importExam(lmsUUId, courseId, quizId, templateId, quitPassword, quitLink) + final Exam exam = fullLmsIntegrationService.importExam( + lmsUUId, + courseId, + quizId, + templateId, + quitPassword, + quitLink, + timezone) .onError(e -> log.error( "Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {}", lmsUUId, courseId, quizId, templateId, e)) @@ -94,11 +107,33 @@ public class LmsIntegrationController { @RequestParam(name = API.LMS_FULL_INTEGRATION_QUIZ_ID, required = true) final String quizId, final HttpServletResponse response) throws IOException { - fullLmsIntegrationService.streamConnectionConfiguration(lmsUUId, courseId, quizId, response.getOutputStream()) - .onError(e -> log.error( - "Failed to stream connection configuration for exam: lmsId:{}, courseId: {}, quizId: {}", - lmsUUId, courseId, quizId, e)) - .getOrThrow(); + final ServletOutputStream outputStream = response.getOutputStream(); + final PipedOutputStream pout; + final PipedInputStream pin; + try { + pout = new PipedOutputStream(); + pin = new PipedInputStream(pout); - } + fullLmsIntegrationService + .streamConnectionConfiguration(lmsUUId, courseId, quizId, pout) + .getOrThrow(); + + IOUtils.copyLarge(pin, outputStream); + + response.setStatus(HttpStatus.OK.value()); + outputStream.flush(); + + } catch (final APIMessage.APIMessageException me) { + response.setStatus(HttpStatus.BAD_REQUEST.value()); + throw me; + } catch (final Exception e) { + log.error( + "Failed to stream connection configuration for exam: lmsId:{}, courseId: {}, quizId: {}", + lmsUUId, courseId, quizId, e); + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + } finally { + outputStream.flush(); + outputStream.close(); + } + } } diff --git a/src/main/resources/config/application-gui.properties b/src/main/resources/config/application-gui.properties index a037785f..d8b94a6b 100644 --- a/src/main/resources/config/application-gui.properties +++ b/src/main/resources/config/application-gui.properties @@ -19,6 +19,7 @@ sebserver.gui.date.displayformat=de sebserver.gui.http.external.scheme=${sebserver.webservice.http.external.scheme} sebserver.gui.http.external.servername=${sebserver.webservice.http.external.servername} sebserver.gui.http.external.port=${sebserver.webservice.http.external.port} +sebserver.gui.http.external.autologin.endpoint=/auto_login sebserver.gui.http.webservice.scheme=http sebserver.gui.http.webservice.servername=localhost diff --git a/src/main/resources/config/application-ws.properties b/src/main/resources/config/application-ws.properties index 5c543031..5d98a08b 100644 --- a/src/main/resources/config/application-ws.properties +++ b/src/main/resources/config/application-ws.properties @@ -47,6 +47,7 @@ sebserver.webservice.http.external.servername= sebserver.webservice.http.external.port= sebserver.webservice.http.redirect.gui=/gui sebserver.webservice.ping.service.strategy=BLOCKING +sebserver.webservice.autologin.endpoint=/auto_login ### webservice API diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java index b4840cc5..2c7bd004 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java @@ -96,6 +96,7 @@ public class ModelObjectJSONGenerator { domainObject = new UserMod( "UUID", 1L, "NAME", "SURNAME", "USERNAME", "newPassword", "confirmNewPassword", "EMAIL", Locale.ENGLISH, DateTimeZone.UTC, + true, true, new HashSet<>(Arrays.asList(UserRole.EXAM_ADMIN.name(), UserRole.EXAM_SUPPORTER.name()))); System.out.println(domainObject.getClass().getSimpleName() + ":"); System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));