From b5cb387061ff5bd872f77a96b5eae53848984676 Mon Sep 17 00:00:00 2001 From: anhefti Date: Fri, 23 Dec 2022 09:01:32 +0100 Subject: [PATCH] SEBSERV-301 --- .../exam/ExamSEBRestrictionSettings.java | 8 +- .../session/impl/ExamSessionControlTask.java | 2 +- .../session/impl/ExamUpdateHandler.java | 129 +++++------------- src/main/resources/messages.properties | 5 +- 4 files changed, 46 insertions(+), 98 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamSEBRestrictionSettings.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamSEBRestrictionSettings.java index 7b66f024..7043f566 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamSEBRestrictionSettings.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamSEBRestrictionSettings.java @@ -75,7 +75,9 @@ public class ExamSEBRestrictionSettings { private final static LocTextKey SEB_RESTRICTION_FORM_BROWSER_KEYS = new LocTextKey("sebserver.exam.form.sebrestriction.browserExamKeys"); private final static LocTextKey SEB_RESTRICTION_FORM_MOODLE_ALT_BEK_KEY = - new LocTextKey("sebserver.exam.form.sebrestriction.ALT_BEK_KEY"); + new LocTextKey("sebserver.exam.form.sebrestriction.MOODLE_ALT_BEK_KEY"); + private final static LocTextKey SEB_RESTRICTION_FORM_MOODLE_BEK_KEY = + new LocTextKey("sebserver.exam.form.sebrestriction.MOODLE_BEK_KEY"); private final static LocTextKey SEB_RESTRICTION_FORM_EDX_WHITE_LIST_PATHS = new LocTextKey("sebserver.exam.form.sebrestriction.WHITELIST_PATHS"); private final static LocTextKey SEB_RESTRICTION_FORM_EDX_PERMISSIONS = @@ -259,7 +261,9 @@ public class ExamSEBRestrictionSettings { .addField(FormBuilder.text( SEBRestriction.ATTR_BROWSER_KEYS, - SEB_RESTRICTION_FORM_BROWSER_KEYS, + (lmsType == lmsType.MOODLE_PLUGIN) + ? SEB_RESTRICTION_FORM_MOODLE_BEK_KEY + : SEB_RESTRICTION_FORM_BROWSER_KEYS, StringUtils.join(sebRestriction.getBrowserExamKeys(), Constants.CARRIAGE_RETURN)) .asArea()) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java index 20f64c97..a66375c0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamSessionControlTask.java @@ -58,7 +58,7 @@ public class ExamSessionControlTask implements DisposableBean { final WebserviceInfo webserviceInfo, @Value("${sebserver.webservice.api.exam.time-prefix:3600000}") final Long examTimePrefix, @Value("${sebserver.webservice.api.exam.time-suffix:3600000}") final Long examTimeSuffix, - @Value("${sebserver.webservice.api.exam.update-interval:1 * * * * *}") final String examTaskCron, + @Value("${sebserver.webservice.api.exam.update-interval:60000}") final String examTaskCron, @Value("${sebserver.webservice.api.exam.update-ping:5000}") final Long pingUpdateRate) { this.examDAO = examDAO; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java index c92b9035..709f362b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamUpdateHandler.java @@ -405,102 +405,43 @@ class ExamUpdateHandler { return this.lmsAPIService .getLmsAPITemplate(lmsSetupId) .flatMap(template -> template.tryRecoverQuizForExam(exam)) - .onError(error -> { - - this.additionalAttributesDAO.saveAdditionalAttribute( - EntityType.EXAM, - exam.id, - Exam.ADDITIONAL_ATTR_QUIZ_RECOVER_ATTEMPTS, - String.valueOf(attempts + 1)) - .onError(error1 -> log.error("Failed to save new attempts: ", error1)); - - if (exam.lmsAvailable == null || exam.isLmsAvailable()) { - this.examDAO.markLMSAvailability(quizId, false, updateId); - } - }) + .onSuccess(recoveredQuizData -> recoverSuccess(updateId, exam, recoveredQuizData)) + .onError(error -> recoverError(quizId, updateId, exam, attempts)) .getOrThrowRuntime("Not Available"); - -// // If this is a Moodle quiz, try to recover from eventually restore of the quiz on the LMS side -// // NOTE: This is a workaround for Moodle quizzes that had have a recovery within the sandbox tool -// // Where potentially quiz identifiers get changed during such a recovery and the SEB Server -// // internal mapping is not working properly anymore. In this case we try to recover from such -// // a case by using the short name of the quiz and search for the quiz within the course with this -// // short name. If one quiz has been found that matches all criteria, we adapt the internal id -// // mapping to this quiz. -// // If recovering fails, this returns null and the calling side must handle the lack of quiz data -// if (lmsTemplate.getType() == LmsType.MOODLE || lmsTemplate.getType() == LmsType.MOODLE_PLUGIN) { -// -// final int attempts = Integer.parseInt(this.additionalAttributesDAO.getAdditionalAttribute( -// EntityType.EXAM, -// exam.id, -// Exam.ADDITIONAL_ATTR_QUIZ_RECOVER_ATTEMPTS) -// .map(AdditionalAttributeRecord::getValue) -// .getOr("0")); -// -// if (attempts >= this.recoverAttempts) { -// if (log.isDebugEnabled()) { -// log.debug("Skip recovering quiz due to too many attempts: {}", exam.getModelId()); -// throw new RuntimeException("Recover attempts reached"); -// } -// } -// -// log.info( -// "Try to recover quiz data for Moodle quiz with internal identifier: {}", -// quizId); -// -// if (exam != null && exam.name != null -// && !exam.name.startsWith(Constants.SQUARE_BRACE_OPEN.toString())) { -// -// log.debug("Found formerName quiz name: {}", exam.name); -// -// // get the course name identifier -// final String shortname = MoodleUtils.getShortname(quizId); -// if (StringUtils.isNotBlank(shortname)) { -// -// log.debug("Using short-name: {} for recovering", shortname); -// -// final QuizData recoveredQuizData = lmsTemplate -// .getQuizzes(new FilterMap()) -// .getOrThrow() -// .stream() -// .filter(quiz -> { -// final String qShortName = MoodleUtils.getShortname(quiz.id); -// return qShortName != null && qShortName.equals(shortname); -// }) -// .filter(quiz -> exam.name.equals(quiz.name)) -// .findAny() -// .get(); -// -// if (recoveredQuizData != null) { -// -// log.debug("Found quiz data for recovering: {}", recoveredQuizData); -// -// // save exam with new external id and quit data -// this.examDAO -// .updateQuizData(exam.id, recoveredQuizData, updateId) -// .getOrThrow(); -// -// log.debug("Successfully recovered exam quiz data to new externalId {}", -// recoveredQuizData.id); -// } -// return recoveredQuizData; -// } -// } -// -// this.additionalAttributesDAO.saveAdditionalAttribute( -// EntityType.EXAM, -// exam.id, -// Exam.ADDITIONAL_ATTR_QUIZ_RECOVER_ATTEMPTS, -// String.valueOf(attempts + 1)) -// .onError(error -> log.error("Failed to save new attempts: ", error)); -// } -// -// if (exam.lmsAvailable == null || exam.isLmsAvailable()) { -// this.examDAO.markLMSAvailability(quizId, false, updateId); -// } -// -// throw new RuntimeException("Not Available"); }); } + private void recoverError(final String quizId, final String updateId, final Exam exam, final int attempts) { + + // increment attempts + this.additionalAttributesDAO.saveAdditionalAttribute( + EntityType.EXAM, + exam.id, + Exam.ADDITIONAL_ATTR_QUIZ_RECOVER_ATTEMPTS, + String.valueOf(attempts + 1)) + .onError(error1 -> log.error("Failed to save new attempts: ", error1)); + + if (exam.lmsAvailable == null || exam.isLmsAvailable()) { + this.examDAO.markLMSAvailability(quizId, false, updateId); + } + } + + private void recoverSuccess(final String updateId, final Exam exam, final QuizData recoveredQuizData) { + if (recoveredQuizData != null) { + + // save exam with new external id and quit data + this.examDAO + .updateQuizData(exam.id, recoveredQuizData, updateId) + .onError(error -> log.error("Failed to save exam for recovered quiz data: ", error)) + .onSuccess(qd -> log.info("Successfully recovered exam from quiz data, {}", qd)) + .getOrThrow(); + + // delete attempts attribute + this.additionalAttributesDAO.delete( + EntityType.EXAM, + exam.id, + Exam.ADDITIONAL_ATTR_QUIZ_RECOVER_ATTEMPTS); + } + } + } diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 4682470c..e90793bb 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -586,7 +586,10 @@ sebserver.exam.form.sebrestriction.PERMISSION_COMPONENTS=Permissions sebserver.exam.form.sebrestriction.PERMISSION_COMPONENTS.tooltip=Define the additional SEB restriction permissions sebserver.exam.form.sebrestriction.USER_BANNING_ENABLED=User Banning sebserver.exam.form.sebrestriction.USER_BANNING_ENABLED.tooltip=Indicates whether the user of a restricted access shall be banned on authentication failure or not -sebserver.exam.form.sebrestriction.ALT_BEK_KEY=SEB Server Browser Exam Key +sebserver.exam.form.sebrestriction.MOODLE_ALT_BEK_KEY=SEB Server Exam Key +sebserver.exam.form.sebrestriction.MOODLE_ALT_BEK_KEY.tootlip=This key is generated by SEB Server and used as Browser Exam Key for Moodle SEB restriction. +sebserver.exam.form.sebrestriction.MOODLE_BEK_KEY=Additional Browser Exam Keys +sebserver.exam.form.sebrestriction.MOODLE_BEK_KEY.tooltip==A comma-separated list of additional SEB Browser Exam Keys
that are checked by the LMS for the restricted SEB access for every request sebserver.exam.form.sebrestriction.whiteListPaths.ABOUT=About sebserver.exam.form.sebrestriction.whiteListPaths.ABOUT.tooltip=The "About" section of the Open edX course