SEBSERV-559 and SEBSERV-552
This commit is contained in:
		
							parent
							
								
									c0ead99e2b
								
							
						
					
					
						commit
						16b2c8deb4
					
				
					 12 changed files with 131 additions and 90 deletions
				
			
		|  | @ -169,7 +169,6 @@ public class MonitoringProctoringService { | ||||||
|             final ScreenProctoringSettings screenProctoringSettings) { |             final ScreenProctoringSettings screenProctoringSettings) { | ||||||
| 
 | 
 | ||||||
|         collectingRooms |         collectingRooms | ||||||
|                 .stream() |  | ||||||
|                 .forEach(room -> updateProctoringAction( |                 .forEach(room -> updateProctoringAction( | ||||||
|                         pageContext, |                         pageContext, | ||||||
|                         proctoringSettings, |                         proctoringSettings, | ||||||
|  | @ -184,7 +183,6 @@ public class MonitoringProctoringService { | ||||||
| 
 | 
 | ||||||
|         if (screenProctoringGroups != null) { |         if (screenProctoringGroups != null) { | ||||||
|             screenProctoringGroups |             screenProctoringGroups | ||||||
|                     .stream() |  | ||||||
|                     .forEach(group -> updateScreenProctoringAction( |                     .forEach(group -> updateScreenProctoringAction( | ||||||
|                             pageContext, |                             pageContext, | ||||||
|                             screenProctoringSettings, |                             screenProctoringSettings, | ||||||
|  | @ -357,8 +355,12 @@ public class MonitoringProctoringService { | ||||||
| 
 | 
 | ||||||
|             // Open SPS Gui redirect URL with login token (jwt token) in new browser tab |             // Open SPS Gui redirect URL with login token (jwt token) in new browser tab | ||||||
|             final String redirectLocation = redirect.getBody() + "/jwt?token=" + tokenRequest.getBody(); |             final String redirectLocation = redirect.getBody() + "/jwt?token=" + tokenRequest.getBody(); | ||||||
|  |             final String script = "window.open("+ redirectLocation + ", 'seb_screen_proctoring')"; | ||||||
|             final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class); |             final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class); | ||||||
|             launcher.openURL(redirectLocation); |             launcher.openURL(redirectLocation); | ||||||
|  | //            RWT.getClient() | ||||||
|  | //                    .getService(JavaScriptExecutor.class) | ||||||
|  | //                    .execute(script); | ||||||
|         } catch (final Exception e) { |         } catch (final Exception e) { | ||||||
|             log.error("Failed to open screen proctoring service group gallery view: ", e); |             log.error("Failed to open screen proctoring service group gallery view: ", e); | ||||||
|             _action.pageContext() |             _action.pageContext() | ||||||
|  |  | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | /* | ||||||
|  |  *  Copyright (c) 2019 ETH Zürich, IT Services | ||||||
|  |  * | ||||||
|  |  *  This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  *  License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  *  file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package ch.ethz.seb.sebserver.webservice.servicelayer.authorization; | ||||||
|  | 
 | ||||||
|  | public final class AdHocAccountData { | ||||||
|  |     public final String userId; | ||||||
|  |     public final String username; | ||||||
|  |     public final String userMail; | ||||||
|  |     public final String firstName; | ||||||
|  |     public final String lastName; | ||||||
|  |     public final String timezone; | ||||||
|  | 
 | ||||||
|  |     public AdHocAccountData( | ||||||
|  |             final String userId, | ||||||
|  |             final String username, | ||||||
|  |             final String userMail, | ||||||
|  |             final String firstName, | ||||||
|  |             final String lastName, | ||||||
|  |             final String timezone) { | ||||||
|  | 
 | ||||||
|  |         this.userId = userId; | ||||||
|  |         this.username = username; | ||||||
|  |         this.userMail = userMail; | ||||||
|  |         this.firstName = firstName; | ||||||
|  |         this.lastName = lastName; | ||||||
|  |         this.timezone = timezone; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -27,7 +27,7 @@ public interface TeacherAccountService { | ||||||
|      */ |      */ | ||||||
|     Result<UserInfo> createNewTeacherAccountForExam( |     Result<UserInfo> createNewTeacherAccountForExam( | ||||||
|             Exam exam, |             Exam exam, | ||||||
|             final FullLmsIntegrationService.AdHocAccountData adHocAccountData); |             final AdHocAccountData adHocAccountData); | ||||||
| 
 | 
 | ||||||
|     /** Get the identifier for certain Teacher account for specified Exam. |     /** Get the identifier for certain Teacher account for specified Exam. | ||||||
|      * |      * | ||||||
|  | @ -37,7 +37,7 @@ public interface TeacherAccountService { | ||||||
|      */ |      */ | ||||||
|     default String getTeacherAccountIdentifier( |     default String getTeacherAccountIdentifier( | ||||||
|             final Exam exam, |             final Exam exam, | ||||||
|             final FullLmsIntegrationService.AdHocAccountData adHocAccountData) { |             final AdHocAccountData adHocAccountData) { | ||||||
| 
 | 
 | ||||||
|         return getTeacherAccountIdentifier( |         return getTeacherAccountIdentifier( | ||||||
|                 String.valueOf(exam.lmsSetupId), |                 String.valueOf(exam.lmsSetupId), | ||||||
|  | @ -62,7 +62,7 @@ public interface TeacherAccountService { | ||||||
|      */ |      */ | ||||||
|     Result<String> getOneTimeTokenForTeacherAccount( |     Result<String> getOneTimeTokenForTeacherAccount( | ||||||
|             Exam exam, |             Exam exam, | ||||||
|             FullLmsIntegrationService.AdHocAccountData adHocAccountData, |             AdHocAccountData adHocAccountData, | ||||||
|             boolean createIfNotExists); |             boolean createIfNotExists); | ||||||
| 
 | 
 | ||||||
|     /** Used to verify a given One Time Access JWT Token. This must have the expected claims and must not be expired |     /** Used to verify a given One Time Access JWT Token. This must have the expected claims and must not be expired | ||||||
|  |  | ||||||
|  | @ -20,6 +20,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServe | ||||||
| public interface UserService { | public interface UserService { | ||||||
| 
 | 
 | ||||||
|     String USERS_INSTITUTION_AS_DEFAULT = "USERS_INSTITUTION_AS_DEFAULT"; |     String USERS_INSTITUTION_AS_DEFAULT = "USERS_INSTITUTION_AS_DEFAULT"; | ||||||
|  | 
 | ||||||
|  |     /** UUID of the internal account that is used for LMS integration related remote call tasks */ | ||||||
|     String LMS_INTEGRATION_CLIENT_UUID = "LMS_INTEGRATION_CLIENT"; |     String LMS_INTEGRATION_CLIENT_UUID = "LMS_INTEGRATION_CLIENT"; | ||||||
|     String LMS_INTEGRATION_CLIENT_NAME = "lmsIntegrationClient"; |     String LMS_INTEGRATION_CLIENT_NAME = "lmsIntegrationClient"; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,10 +20,10 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Cryptor; | import ch.ethz.seb.sebserver.gbl.util.Cryptor; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.TeacherAccountService; | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.TeacherAccountService; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService; |  | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService; | import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService; | ||||||
| import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminAPIClientDetails; | import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminAPIClientDetails; | ||||||
| import io.jsonwebtoken.Claims; | import io.jsonwebtoken.Claims; | ||||||
|  | @ -91,7 +91,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService { | ||||||
|     @Override |     @Override | ||||||
|     public Result<UserInfo> createNewTeacherAccountForExam( |     public Result<UserInfo> createNewTeacherAccountForExam( | ||||||
|             final Exam exam, |             final Exam exam, | ||||||
|             final FullLmsIntegrationService.AdHocAccountData adHocAccountData) { |             final AdHocAccountData adHocAccountData) { | ||||||
| 
 | 
 | ||||||
|         return Result.tryCatch(() -> { |         return Result.tryCatch(() -> { | ||||||
| 
 | 
 | ||||||
|  | @ -130,7 +130,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService { | ||||||
|     @Override |     @Override | ||||||
|     public Result<String> getOneTimeTokenForTeacherAccount( |     public Result<String> getOneTimeTokenForTeacherAccount( | ||||||
|             final Exam exam, |             final Exam exam, | ||||||
|             final FullLmsIntegrationService.AdHocAccountData adHocAccountData, |             final AdHocAccountData adHocAccountData, | ||||||
|             final boolean createIfNotExists) { |             final boolean createIfNotExists) { | ||||||
| 
 | 
 | ||||||
|         return this.userDAO |         return this.userDAO | ||||||
|  | @ -186,7 +186,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService { | ||||||
|     private UserInfo handleAccountDoesNotExistYet( |     private UserInfo handleAccountDoesNotExistYet( | ||||||
|             final boolean createIfNotExists, |             final boolean createIfNotExists, | ||||||
|             final Exam exam, |             final Exam exam, | ||||||
|             final FullLmsIntegrationService.AdHocAccountData adHocAccountData) { |             final AdHocAccountData adHocAccountData) { | ||||||
| 
 | 
 | ||||||
|         if (createIfNotExists) { |         if (createIfNotExists) { | ||||||
|             return this |             return this | ||||||
|  | @ -212,6 +212,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService { | ||||||
|                             "Failed to apply ad-hoc-teacher account to supporter list of exam: {} user: {}", |                             "Failed to apply ad-hoc-teacher account to supporter list of exam: {} user: {}", | ||||||
|                             exam, account, error)); |                             exam, account, error)); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         return account; |         return account; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -219,7 +220,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService { | ||||||
| 
 | 
 | ||||||
|         final String subjectClaim = UUID.randomUUID().toString(); |         final String subjectClaim = UUID.randomUUID().toString(); | ||||||
|         userDAO.changePassword(account.uuid, subjectClaim); |         userDAO.changePassword(account.uuid, subjectClaim); | ||||||
|         synchronizeSPSUserForExam(account, examId); |         this.screenProctoringService.updateExamOnScreenProctoringService(examId); | ||||||
| 
 | 
 | ||||||
|         final Map<String, Object> claims = new HashMap<>(); |         final Map<String, Object> claims = new HashMap<>(); | ||||||
|         claims.put(USER_CLAIM, account.uuid); |         claims.put(USER_CLAIM, account.uuid); | ||||||
|  | @ -275,10 +276,4 @@ public class TeacherAccountServiceImpl implements TeacherAccountService { | ||||||
|         return claims; |         return claims; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private UserInfo synchronizeSPSUserForExam(final UserInfo account, final Long examId) { |  | ||||||
|         if (this.screenProctoringService.isScreenProctoringEnabled(examId)) { |  | ||||||
|             this.screenProctoringService.synchronizeSPSUserForExam(examId); |  | ||||||
|         } |  | ||||||
|         return account; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,7 +16,6 @@ import java.util.Objects; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.*; | import ch.ethz.seb.sebserver.gbl.model.exam.*; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; | import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService; |  | ||||||
| import org.apache.commons.lang3.BooleanUtils; | import org.apache.commons.lang3.BooleanUtils; | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.joda.time.DateTime; | import org.joda.time.DateTime; | ||||||
|  | @ -299,7 +298,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService { | ||||||
|                 !Objects.equals(examConfig.templateId, examTemplate.configTemplateId)) { |                 !Objects.equals(examConfig.templateId, examTemplate.configTemplateId)) { | ||||||
| 
 | 
 | ||||||
|             final String newName = (examConfig != null && examConfig.name.equals(configName)) |             final String newName = (examConfig != null && examConfig.name.equals(configName)) | ||||||
|                     ? examConfig.name + "_" |                     ? examConfig.name + "_" + DateTime.now(DateTimeZone.UTC).toString(Constants.STANDARD_DATE_FORMATTER) | ||||||
|                     : configName; |                     : configName; | ||||||
| 
 | 
 | ||||||
|             final ConfigurationNode config = new ConfigurationNode( |             final ConfigurationNode config = new ConfigurationNode( | ||||||
|  |  | ||||||
|  | @ -11,12 +11,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms; | ||||||
| import java.io.OutputStream; | import java.io.OutputStream; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| 
 | 
 | ||||||
| import ch.ethz.seb.sebserver.gbl.api.API; |  | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.Exam; | import ch.ethz.seb.sebserver.gbl.model.exam.Exam; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; |  | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Utils; | import ch.ethz.seb.sebserver.gbl.util.Utils; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateChangeEvent; | import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateChangeEvent; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent; | import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent; | ||||||
|  | @ -26,7 +25,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; | ||||||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||||||
| import com.fasterxml.jackson.annotation.JsonProperty; | import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
| import org.springframework.context.event.EventListener; | import org.springframework.context.event.EventListener; | ||||||
| import org.springframework.web.bind.annotation.RequestParam; |  | ||||||
| 
 | 
 | ||||||
| public interface FullLmsIntegrationService { | public interface FullLmsIntegrationService { | ||||||
| 
 | 
 | ||||||
|  | @ -79,30 +77,7 @@ public interface FullLmsIntegrationService { | ||||||
|             AdHocAccountData adHocAccountData); |             AdHocAccountData adHocAccountData); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     final class AdHocAccountData { |  | ||||||
|         public final String userId; |  | ||||||
|         public final String username; |  | ||||||
|         public final String userMail; |  | ||||||
|         public final String firstName; |  | ||||||
|         public final String lastName; |  | ||||||
|         public final String timezone; |  | ||||||
| 
 | 
 | ||||||
|         public AdHocAccountData( |  | ||||||
|                 final String userId, |  | ||||||
|                 final String username, |  | ||||||
|                 final String userMail, |  | ||||||
|                 final String firstName, |  | ||||||
|                 final String lastName, |  | ||||||
|                 final String timezone) { |  | ||||||
| 
 |  | ||||||
|             this.userId = userId; |  | ||||||
|             this.username = username; |  | ||||||
|             this.userMail = userMail; |  | ||||||
|             this.firstName = firstName; |  | ||||||
|             this.lastName = lastName; |  | ||||||
|             this.timezone = timezone; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     @JsonIgnoreProperties(ignoreUnknown = true) |     @JsonIgnoreProperties(ignoreUnknown = true) | ||||||
|     final class ExamData { |     final class ExamData { | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig; | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||||
| import ch.ethz.seb.sebserver.gbl.util.Result; | import ch.ethz.seb.sebserver.gbl.util.Result; | ||||||
| import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.TeacherAccountServiceImpl; | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.TeacherAccountServiceImpl; | ||||||
|  | @ -455,14 +456,22 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService | ||||||
|             final String quizId, |             final String quizId, | ||||||
|             final String examData) { |             final String examData) { | ||||||
| 
 | 
 | ||||||
|         final String internalQuizId = MoodleUtils.getInternalQuizId( |         if (StringUtils.isNotBlank(examData)) { | ||||||
|                 quizId, |             return lmsAPITemplate | ||||||
|                 courseId, |                     .getQuizDataForRemoteImport(examData) | ||||||
|                 null, |                     .getOrThrow(); | ||||||
|                 null); |         } else { | ||||||
| 
 | 
 | ||||||
|         return lmsAPITemplate.getQuizDataForRemoteImport(examData) |             final String internalQuizId = MoodleUtils.getInternalQuizId( | ||||||
|                 .getOrThrow(); |                     quizId, | ||||||
|  |                     courseId, | ||||||
|  |                     null, | ||||||
|  |                     null); | ||||||
|  | 
 | ||||||
|  |             return lmsAPITemplate.getQuiz(internalQuizId) | ||||||
|  |                     .getOrThrow(); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -71,6 +71,9 @@ public interface ScreenProctoringService extends SessionUpdateTask { | ||||||
|     @EventListener(ExamFinishedEvent.class) |     @EventListener(ExamFinishedEvent.class) | ||||||
|     void notifyExamFinished(ExamFinishedEvent event); |     void notifyExamFinished(ExamFinishedEvent event); | ||||||
| 
 | 
 | ||||||
|  |     @EventListener(ExamResetEvent.class) | ||||||
|  |     void notifyExamReset(ExamResetEvent event); | ||||||
|  | 
 | ||||||
|     /** This is being called just before an Exam gets deleted on the permanent storage. |     /** This is being called just before an Exam gets deleted on the permanent storage. | ||||||
|      * This deactivates and dispose or deletes all exam relevant domain entities on the SPS service side. |      * This deactivates and dispose or deletes all exam relevant domain entities on the SPS service side. | ||||||
|      * |      * | ||||||
|  |  | ||||||
|  | @ -102,6 +102,12 @@ class ScreenProctoringAPIBinding { | ||||||
|             String ATTR_PRIVILEGES = "privileges"; |             String ATTR_PRIVILEGES = "privileges"; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         interface PRIVILEGE_FLAGS { | ||||||
|  |             String READ = "r"; | ||||||
|  |             String MODIFY = "m"; | ||||||
|  |             String WRITE = "w"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         /** The screen proctoring service client-access API attribute names */ |         /** The screen proctoring service client-access API attribute names */ | ||||||
|         interface SEB_ACCESS { |         interface SEB_ACCESS { | ||||||
|             String ATTR_UUID = "uuid"; |             String ATTR_UUID = "uuid"; | ||||||
|  | @ -380,8 +386,6 @@ class ScreenProctoringAPIBinding { | ||||||
| 
 | 
 | ||||||
|     void synchronizeUserAccounts(final Exam exam) { |     void synchronizeUserAccounts(final Exam exam) { | ||||||
|         try { |         try { | ||||||
|             final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id); |  | ||||||
|             final SPSData spsData = this.getSPSData(exam.id); |  | ||||||
| 
 | 
 | ||||||
|             exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate)); |             exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate)); | ||||||
|             if (exam.owner != null) { |             if (exam.owner != null) { | ||||||
|  | @ -389,7 +393,7 @@ class ScreenProctoringAPIBinding { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } catch (final Exception e) { |         } catch (final Exception e) { | ||||||
|             log.error("Failed to synchronize user accounts with SPS for exam: {}", exam); |             log.error("Failed to synchronize user accounts with SPS for exam: {}", exam, e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     void deleteSPSUser(final String userUUID) { |     void deleteSPSUser(final String userUUID) { | ||||||
|  | @ -433,11 +437,7 @@ class ScreenProctoringAPIBinding { | ||||||
|                     .build() |                     .build() | ||||||
|                     .toUriString(); |                     .toUriString(); | ||||||
| 
 | 
 | ||||||
|             final List<String> userIds = new ArrayList<>(exam.supporter); |             final List<String> supporterIds = getSupporterIds(exam); | ||||||
|             if (exam.owner != null) { |  | ||||||
|                 userIds.add(exam.owner); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             final ExamUpdate examUpdate = new ExamUpdate( |             final ExamUpdate examUpdate = new ExamUpdate( | ||||||
|                     exam.name, |                     exam.name, | ||||||
|                     exam.getDescription(), |                     exam.getDescription(), | ||||||
|  | @ -445,7 +445,7 @@ class ScreenProctoringAPIBinding { | ||||||
|                     exam.getType().name(), |                     exam.getType().name(), | ||||||
|                     exam.startTime != null ? exam.startTime.getMillis() : null, |                     exam.startTime != null ? exam.startTime.getMillis() : null, | ||||||
|                     exam.endTime != null ? exam.endTime.getMillis() : null, |                     exam.endTime != null ? exam.endTime.getMillis() : null, | ||||||
|                     userIds); |                     supporterIds); | ||||||
| 
 | 
 | ||||||
|             final String jsonExamUpdate = this.jsonMapper.writeValueAsString(examUpdate); |             final String jsonExamUpdate = this.jsonMapper.writeValueAsString(examUpdate); | ||||||
| 
 | 
 | ||||||
|  | @ -796,17 +796,15 @@ class ScreenProctoringAPIBinding { | ||||||
| 
 | 
 | ||||||
|             if (activityRequest.getStatusCode() != HttpStatus.OK) { |             if (activityRequest.getStatusCode() != HttpStatus.OK) { | ||||||
|                 final String body = activityRequest.getBody(); |                 final String body = activityRequest.getBody(); | ||||||
|                 if (body != null && body.contains("Activation argument mismatch")) { |                 if (body != null && !body.contains("Activation argument mismatch")) { | ||||||
|                     return; |                     log.warn("Failed to synchronize activity for user account on SPS: {}", activityRequest); | ||||||
|                 } |                 } | ||||||
| 
 |  | ||||||
|                 log.warn("Failed to synchronize activity for user account on SPS: {}", activityRequest); |  | ||||||
|             } else { |             } else { | ||||||
|                 log.info("Successfully synchronize activity for user account on SPS for user: {}", userUUID); |                 log.info("Successfully synchronize activity for user account on SPS for user: {}", userUUID); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         } catch (final Exception e) { |         } catch (final Exception e) { | ||||||
|             log.error("Failed to synchronize user account with SPS for user: {}", userUUID); |             log.error("Failed to synchronize user account with SPS for user: {}", userUUID, e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -931,18 +929,14 @@ class ScreenProctoringAPIBinding { | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
| 
 | 
 | ||||||
|             final List<String> userIds = new ArrayList<>(exam.supporter); |             final List<String> supporterIds = getSupporterIds(exam); | ||||||
|             if (exam.owner != null) { |  | ||||||
|                 userIds.add(exam.owner); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             final String uri = UriComponentsBuilder |             final String uri = UriComponentsBuilder | ||||||
|                     .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL()) |                     .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL()) | ||||||
|                     .path(SPS_API.EXAM_ENDPOINT) |                     .path(SPS_API.EXAM_ENDPOINT) | ||||||
|                     .build().toUriString(); |                     .build().toUriString(); | ||||||
| 
 | 
 | ||||||
|             final String uuid = createExamUUID(exam); |             final String uuid = createExamUUID(exam); | ||||||
|             final MultiValueMap<String, String> params = createExamCreationParams(exam, uuid, userIds); |             final MultiValueMap<String, String> params = createExamCreationParams(exam, uuid, supporterIds); | ||||||
|             final String paramsFormEncoded = Utils.toAppFormUrlEncodedBodyForSPService(params); |             final String paramsFormEncoded = Utils.toAppFormUrlEncodedBodyForSPService(params); | ||||||
| 
 | 
 | ||||||
|             final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded); |             final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded); | ||||||
|  | @ -976,7 +970,7 @@ class ScreenProctoringAPIBinding { | ||||||
|     private static MultiValueMap<String, String> createExamCreationParams( |     private static MultiValueMap<String, String> createExamCreationParams( | ||||||
|             final Exam exam, |             final Exam exam, | ||||||
|             final String uuid, |             final String uuid, | ||||||
|             final List<String> userIds) { |             final List<String> supporterIds) { | ||||||
| 
 | 
 | ||||||
|         final MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); |         final MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); | ||||||
|         params.add(SPS_API.EXAM.ATTR_UUID, uuid); |         params.add(SPS_API.EXAM.ATTR_UUID, uuid); | ||||||
|  | @ -985,8 +979,8 @@ class ScreenProctoringAPIBinding { | ||||||
|             params.add(SPS_API.EXAM.ATTR_DESCRIPTION, exam.getDescription()); |             params.add(SPS_API.EXAM.ATTR_DESCRIPTION, exam.getDescription()); | ||||||
|         } |         } | ||||||
|         params.add(SPS_API.EXAM.ATTR_URL, exam.getStartURL()); |         params.add(SPS_API.EXAM.ATTR_URL, exam.getStartURL()); | ||||||
|         if (!userIds.isEmpty()) { |         if (!supporterIds.isEmpty()) { | ||||||
|             params.add(SPS_API.EXAM.ATTR_USER_IDS, StringUtils.join(userIds, Constants.LIST_SEPARATOR)); |             params.add(SPS_API.EXAM.ATTR_USER_IDS, StringUtils.join(supporterIds, Constants.LIST_SEPARATOR)); | ||||||
|         } |         } | ||||||
|         params.add(SPS_API.EXAM.ATTR_TYPE, exam.getType().name()); |         params.add(SPS_API.EXAM.ATTR_TYPE, exam.getType().name()); | ||||||
|         params.add(SPS_API.EXAM.ATTR_START_TIME, String.valueOf(exam.startTime.getMillis())); |         params.add(SPS_API.EXAM.ATTR_START_TIME, String.valueOf(exam.startTime.getMillis())); | ||||||
|  | @ -1199,6 +1193,14 @@ class ScreenProctoringAPIBinding { | ||||||
|         return this.apiTemplate; |         return this.apiTemplate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private static List<String> getSupporterIds(final Exam exam) { | ||||||
|  |         final List<String> supporterIds = new ArrayList<>(exam.supporter); | ||||||
|  |         if (exam.owner != null && !UserService.LMS_INTEGRATION_CLIENT_UUID.equals(exam.owner)) { | ||||||
|  |             supporterIds.add(exam.owner); | ||||||
|  |         } | ||||||
|  |         return supporterIds; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     final static class ScreenProctoringServiceOAuthTemplate { |     final static class ScreenProctoringServiceOAuthTemplate { | ||||||
| 
 | 
 | ||||||
|         private static final String GRANT_TYPE = "password"; |         private static final String GRANT_TYPE = "password"; | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import ch.ethz.seb.sebserver.gbl.model.Activatable; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.EntityKey; | import ch.ethz.seb.sebserver.gbl.model.EntityKey; | ||||||
| import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent; | import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.session.*; | ||||||
| import org.apache.commons.lang3.BooleanUtils; | import org.apache.commons.lang3.BooleanUtils; | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
|  | @ -47,10 +48,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ScreenProctoringGroupDA | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ScreenProctoringGroupDAOImpl.AllGroupsFullException; | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ScreenProctoringGroupDAOImpl.AllGroupsFullException; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent; |  | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamStartedEvent; |  | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService; |  | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService; |  | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService; | import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ScreenProctoringAPIBinding.SPSData; | import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ScreenProctoringAPIBinding.SPSData; | ||||||
| 
 | 
 | ||||||
|  | @ -270,7 +267,8 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void synchronizeSPSUserForExam(final Long examId) { |     public void synchronizeSPSUserForExam(final Long examId) { | ||||||
|         this.examDAO.byPK(examId) |         this.examDAO | ||||||
|  |                 .byPK(examId) | ||||||
|                 .onSuccess(this.screenProctoringAPIBinding::synchronizeUserAccounts) |                 .onSuccess(this.screenProctoringAPIBinding::synchronizeUserAccounts) | ||||||
|                 .onError(error -> log.error("Failed to synchronize SPS user accounts for exam: {}", examId, error)); |                 .onError(error -> log.error("Failed to synchronize SPS user accounts for exam: {}", examId, error)); | ||||||
|     } |     } | ||||||
|  | @ -299,14 +297,26 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void notifyExamFinished(final ExamFinishedEvent event) { |     public void notifyExamFinished(final ExamFinishedEvent event) { | ||||||
|         final Exam exam = event.exam; | 
 | ||||||
|         if (!this.isScreenProctoringEnabled(exam.id) || |         if (!this.isScreenProctoringEnabled(event.exam.id) || | ||||||
|                 !BooleanUtils.toBoolean(exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) { |                 !BooleanUtils.toBoolean(event.exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (exam.status == Exam.ExamStatus.FINISHED) { |         if (event.exam.status != Exam.ExamStatus.UP_COMING) { | ||||||
|             this.screenProctoringAPIBinding.deactivateScreenProctoring(exam); |             this.screenProctoringAPIBinding.deactivateScreenProctoring(event.exam); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void notifyExamReset(final ExamResetEvent event) { | ||||||
|  |         if (!this.isScreenProctoringEnabled(event.exam.id) || | ||||||
|  |                 BooleanUtils.toBoolean(event.exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (event.exam.status != Exam.ExamStatus.UP_COMING) { | ||||||
|  |             this.screenProctoringAPIBinding.activateScreenProctoring(event.exam); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,8 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage; | ||||||
| import ch.ethz.seb.sebserver.gbl.model.exam.Exam; | import ch.ethz.seb.sebserver.gbl.model.exam.Exam; | ||||||
| import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; | ||||||
| import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | import ch.ethz.seb.sebserver.webservice.WebserviceInfo; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AdHocAccountData; | ||||||
|  | import ch.ethz.seb.sebserver.webservice.servicelayer.dao.NoResourceFoundException; | ||||||
| import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService; | import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService; | ||||||
| import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||||
| import org.apache.commons.lang3.BooleanUtils; | import org.apache.commons.lang3.BooleanUtils; | ||||||
|  | @ -89,15 +91,23 @@ public class LmsIntegrationController { | ||||||
|                         examData) |                         examData) | ||||||
|                 .onError(e -> { |                 .onError(e -> { | ||||||
|                     log.error( |                     log.error( | ||||||
|                             "Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {} error: {}", |                             "Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {} error: ", | ||||||
|                             lmsUUId, courseId, quizId, templateId, e.getMessage()); |                             lmsUUId, courseId, quizId, templateId, e); | ||||||
|                     log.info("Rollback Exam creation..."); |                     log.info("Rollback Exam creation..."); | ||||||
|                     fullLmsIntegrationService.deleteExam(lmsUUId, courseId, quizId) |                     fullLmsIntegrationService.deleteExam(lmsUUId, courseId, quizId) | ||||||
|                             .onError(error -> log.error("Failed to rollback auto Exam import: ", error)); |                             .onError(error -> { | ||||||
|  |                                 if (error instanceof NoResourceFoundException) { | ||||||
|  |                                     log.info("No Exam found for rollback."); | ||||||
|  |                                 } else { | ||||||
|  |                                     log.error("Failed to rollback auto Exam import: ", error); | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|                 }) |                 }) | ||||||
|                 .getOrThrow(); |                 .getOr(null); | ||||||
| 
 | 
 | ||||||
|         log.info("Auto import of exam successful: {}", exam); |         if (exam != null) { | ||||||
|  |             log.info("Auto import of exam successful: {}", exam); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @RequestMapping( |     @RequestMapping( | ||||||
|  | @ -181,7 +191,7 @@ public class LmsIntegrationController { | ||||||
|             @RequestParam(name = API.LMS_FULL_INTEGRATION_TIME_ZONE, required = false) final String timezone, |             @RequestParam(name = API.LMS_FULL_INTEGRATION_TIME_ZONE, required = false) final String timezone, | ||||||
|             final HttpServletResponse response) { |             final HttpServletResponse response) { | ||||||
| 
 | 
 | ||||||
|         final FullLmsIntegrationService.AdHocAccountData adHocAccountData = new FullLmsIntegrationService.AdHocAccountData( |         final AdHocAccountData adHocAccountData = new AdHocAccountData( | ||||||
|                 userId, |                 userId, | ||||||
|                 username, |                 username, | ||||||
|                 userMail, |                 userMail, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti