SEBSERV-417 and SEBSP-111
This commit is contained in:
		
							parent
							
								
									3a5129f796
								
							
						
					
					
						commit
						c161e3c5ef
					
				
					 13 changed files with 414 additions and 168 deletions
				
			
		| 
						 | 
					@ -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";
 | 
				
			||||||
 | 
					    String LMS_INTEGRATION_CLIENT_UUID = "LMS_INTEGRATION_CLIENT";
 | 
				
			||||||
 | 
					    String LMS_INTEGRATION_CLIENT_NAME = "lmsIntegrationClient";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** Use this to get the current User within a request-response thread cycle.
 | 
					    /** Use this to get the current User within a request-response thread cycle.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,7 @@ public class UserServiceImpl implements UserService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
 | 
					    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public interface ExtractUserFromAuthenticationStrategy {
 | 
					    public interface ExtractUserFromAuthenticationStrategy {
 | 
				
			||||||
        SEBServerUser extract(Principal principal);
 | 
					        SEBServerUser extract(Principal principal);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -137,7 +138,13 @@ public class UserServiceImpl implements UserService {
 | 
				
			||||||
        if ("lmsClient".equals(name)) {
 | 
					        if ("lmsClient".equals(name)) {
 | 
				
			||||||
            return new SEBServerUser(
 | 
					            return new SEBServerUser(
 | 
				
			||||||
                    -1L,
 | 
					                    -1L,
 | 
				
			||||||
                    new UserInfo("LMS_INTEGRATION_CLIENT", -1L, null, "lmsIntegrationClient", "lmsIntegrationClient", "lmsIntegrationClient", null,
 | 
					                    new UserInfo(
 | 
				
			||||||
 | 
					                            LMS_INTEGRATION_CLIENT_UUID,
 | 
				
			||||||
 | 
					                            -1L,
 | 
				
			||||||
 | 
					                            null,
 | 
				
			||||||
 | 
					                            LMS_INTEGRATION_CLIENT_NAME,
 | 
				
			||||||
 | 
					                            LMS_INTEGRATION_CLIENT_NAME,
 | 
				
			||||||
 | 
					                            LMS_INTEGRATION_CLIENT_NAME, null,
 | 
				
			||||||
                            false,
 | 
					                            false,
 | 
				
			||||||
                            false,
 | 
					                            false,
 | 
				
			||||||
                            true,
 | 
					                            true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,12 @@ public interface LmsSetupDAO extends ActivatableEntityDAO<LmsSetup, LmsSetup>, B
 | 
				
			||||||
     * @return Result refers to the specified LMS Setup or to en error when happened */
 | 
					     * @return Result refers to the specified LMS Setup or to en error when happened */
 | 
				
			||||||
    Result<LmsSetup> setIntegrationActive(Long lmsSetupId, boolean active);
 | 
					    Result<LmsSetup> setIntegrationActive(Long lmsSetupId, boolean active);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean isIntegrationActive(Long lmsSetupId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Result<Collection<Long>> idsOfActiveWithFullIntegration(Long institutionId);
 | 
					    Result<Collection<Long>> idsOfActiveWithFullIntegration(Long institutionId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Result<Collection<Long>> allIdsFullIntegration();
 | 
					    Result<Collection<Long>> allIdsFullIntegration();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -317,6 +317,21 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
 | 
				
			||||||
                .onError(TransactionHandler::rollback);
 | 
					                .onError(TransactionHandler::rollback);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    @Transactional(readOnly = true)
 | 
				
			||||||
 | 
					    public boolean isIntegrationActive(final Long lmsSetupId) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return this.lmsSetupRecordMapper.countByExample()
 | 
				
			||||||
 | 
					                    .where(LmsSetupRecordDynamicSqlSupport.id, isEqualTo(lmsSetupId))
 | 
				
			||||||
 | 
					                    .and(integrationActive, isEqualTo(BooleanUtils.toInteger(true)))
 | 
				
			||||||
 | 
					                    .build()
 | 
				
			||||||
 | 
					                    .execute() > 0;
 | 
				
			||||||
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
 | 
					            log.warn("Failed to verify if full LMS integration is active: {}", e.getMessage());
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    @Transactional(readOnly = true)
 | 
					    @Transactional(readOnly = true)
 | 
				
			||||||
    public boolean isActive(final String modelId) {
 | 
					    public boolean isActive(final String modelId) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ public interface FullLmsIntegrationService {
 | 
				
			||||||
    @EventListener
 | 
					    @EventListener
 | 
				
			||||||
    void notifyLmsSetupChange(final LmsSetupChangeEvent event);
 | 
					    void notifyLmsSetupChange(final LmsSetupChangeEvent event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Result<LmsSetup> applyLMSSetupDeactivation(LmsSetup lmsSetup);
 | 
					    //Result<LmsSetup> applyLMSSetupDeactivation(LmsSetup lmsSetup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @EventListener
 | 
					    @EventListener
 | 
				
			||||||
    void notifyExamTemplateChange(final ExamTemplateChangeEvent event);
 | 
					    void notifyExamTemplateChange(final ExamTemplateChangeEvent event);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,6 +73,6 @@ public interface SEBRestrictionService {
 | 
				
			||||||
    @EventListener
 | 
					    @EventListener
 | 
				
			||||||
    void notifyLmsSetupChange(final LmsSetupChangeEvent event);
 | 
					    void notifyLmsSetupChange(final LmsSetupChangeEvent event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Result<LmsSetup> applyLMSSetupDeactivation(LmsSetup lmsSetup);
 | 
					    Result<LmsSetup> releaseAllRestrictionsOf(LmsSetup lmsSetup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -148,7 +148,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
 | 
				
			||||||
        return Result.tryCatch(() -> {
 | 
					        return Result.tryCatch(() -> {
 | 
				
			||||||
            final LmsSetup lmsSetup = lmsSetupDAO.byPK(exam.lmsSetupId).getOrThrow();
 | 
					            final LmsSetup lmsSetup = lmsSetupDAO.byPK(exam.lmsSetupId).getOrThrow();
 | 
				
			||||||
            if (lmsSetup.lmsType.features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
 | 
					            if (lmsSetup.lmsType.features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
 | 
				
			||||||
                return this.applyExamData(exam, false);
 | 
					                return this.applyExamData(exam, !exam.active);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return exam;
 | 
					            return exam;
 | 
				
			||||||
| 
						 | 
					@ -183,17 +183,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
 | 
				
			||||||
                    .map(data -> reapplyExistingExams(data,lmsSetup))
 | 
					                    .map(data -> reapplyExistingExams(data,lmsSetup))
 | 
				
			||||||
                    .onError(error -> log.warn("Failed to update LMS integration for: {} error {}", lmsSetup, error.getMessage()))
 | 
					                    .onError(error -> log.warn("Failed to update LMS integration for: {} error {}", lmsSetup, error.getMessage()))
 | 
				
			||||||
                    .onSuccess(data -> log.debug("Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
 | 
					                    .onSuccess(data -> log.debug("Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
 | 
				
			||||||
        }
 | 
					        } else if (event.activation == Activatable.ActivationAction.DEACTIVATE) {
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Result<LmsSetup> applyLMSSetupDeactivation(final LmsSetup lmsSetup) {
 | 
					 | 
				
			||||||
        if (!lmsSetup.getLmsType().features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
 | 
					 | 
				
			||||||
            return Result.of(lmsSetup);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Result.tryCatch(() -> {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // remove all active exam data for involved exams before deactivate them
 | 
					            // remove all active exam data for involved exams before deactivate them
 | 
				
			||||||
            this.examDAO
 | 
					            this.examDAO
 | 
				
			||||||
                    .allActiveForLMSSetup(Arrays.asList(lmsSetup.id))
 | 
					                    .allActiveForLMSSetup(Arrays.asList(lmsSetup.id))
 | 
				
			||||||
| 
						 | 
					@ -203,13 +193,10 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
 | 
				
			||||||
                                .map(e -> applyExamData(e, true))
 | 
					                                .map(e -> applyExamData(e, true))
 | 
				
			||||||
                                .onError(error -> log.warn("Failed delete teacher accounts for exam: {}", exam.name));
 | 
					                                .onError(error -> log.warn("Failed delete teacher accounts for exam: {}", exam.name));
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					            // delete full integration on Moodle side due to deactivation
 | 
				
			||||||
            // delete full integration on Moodle side before deactivate LMS Setup
 | 
					 | 
				
			||||||
            this.deleteFullLmsIntegration(lmsSetup.id)
 | 
					            this.deleteFullLmsIntegration(lmsSetup.id)
 | 
				
			||||||
                    .getOrThrow();
 | 
					                    .getOrThrow();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
            return lmsSetup;
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
| 
						 | 
					@ -302,6 +289,10 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
 | 
				
			||||||
                        return false;
 | 
					                        return false;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!lmsSetupDAO.isIntegrationActive(lmsSetupId)) {
 | 
				
			||||||
 | 
					                        return true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    lmsAPITemplateCacheService.getLmsAPITemplate(lmsSetupId)
 | 
					                    lmsAPITemplateCacheService.getLmsAPITemplate(lmsSetupId)
 | 
				
			||||||
                            .getOrThrow()
 | 
					                            .getOrThrow()
 | 
				
			||||||
                            .deleteConnectionDetails()
 | 
					                            .deleteConnectionDetails()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,6 +114,7 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService {
 | 
				
			||||||
        if (!lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) {
 | 
					        if (!lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if (event.activation == Activatable.ActivationAction.ACTIVATE) {
 | 
					            if (event.activation == Activatable.ActivationAction.ACTIVATE) {
 | 
				
			||||||
                examDAO.allActiveForLMSSetup(Arrays.asList(lmsSetup.id))
 | 
					                examDAO.allActiveForLMSSetup(Arrays.asList(lmsSetup.id))
 | 
				
			||||||
| 
						 | 
					@ -125,6 +126,11 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService {
 | 
				
			||||||
                                log.warn("Failed to update SEB restriction for exam: {} error: {}", exam.name, e.getMessage());
 | 
					                                log.warn("Failed to update SEB restriction for exam: {} error: {}", exam.name, e.getMessage());
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
 | 
					            } else if (event.activation == Activatable.ActivationAction.DEACTIVATE) {
 | 
				
			||||||
 | 
					                releaseAllRestrictionsOf(lmsSetup)
 | 
				
			||||||
 | 
					                        .onError(error -> log.warn(
 | 
				
			||||||
 | 
					                                "Failed to remove all SEB Restrictions on LMS Setup deactivation: {}",
 | 
				
			||||||
 | 
					                                error.getMessage()));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (final Exception e) {
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
            log.error("Failed to update SEB restriction for re-activated exams: {}", e.getMessage());
 | 
					            log.error("Failed to update SEB restriction for re-activated exams: {}", e.getMessage());
 | 
				
			||||||
| 
						 | 
					@ -132,8 +138,7 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Result<LmsSetup> applyLMSSetupDeactivation(final LmsSetup lmsSetup) {
 | 
					    public Result<LmsSetup> releaseAllRestrictionsOf(final LmsSetup lmsSetup) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Result.tryCatch(() -> {
 | 
					        return Result.tryCatch(() -> {
 | 
				
			||||||
            // only relevant for LMS Setups with SEB restriction feature
 | 
					            // only relevant for LMS Setups with SEB restriction feature
 | 
				
			||||||
            if (!lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) {
 | 
					            if (!lmsSetup.lmsType.features.contains(Features.SEB_RESTRICTION)) {
 | 
				
			||||||
| 
						 | 
					@ -274,7 +279,6 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService {
 | 
				
			||||||
            // create new ones if needed
 | 
					            // create new ones if needed
 | 
				
			||||||
            sebRestriction.additionalProperties
 | 
					            sebRestriction.additionalProperties
 | 
				
			||||||
                    .entrySet()
 | 
					                    .entrySet()
 | 
				
			||||||
                    .stream()
 | 
					 | 
				
			||||||
                    .forEach(entry -> this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
					                    .forEach(entry -> this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
                            EntityType.EXAM,
 | 
					                            EntityType.EXAM,
 | 
				
			||||||
                            exam.id,
 | 
					                            exam.id,
 | 
				
			||||||
| 
						 | 
					@ -332,7 +336,7 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService {
 | 
				
			||||||
            log.debug("ExamDeletionEvent received, process releaseSEBClientRestriction...");
 | 
					            log.debug("ExamDeletionEvent received, process releaseSEBClientRestriction...");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        event.ids.stream().forEach(this::processExamDeletion);
 | 
					        event.ids.forEach(this::processExamDeletion);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Result<Exam> processExamDeletion(final Long examId) {
 | 
					    private Result<Exam> processExamDeletion(final Long examId) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,8 @@ import java.util.Collection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
 | 
					import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
 | 
					import ch.ethz.seb.sebserver.gbl.model.EntityKey;
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsSetupChangeEvent;
 | 
				
			||||||
import org.springframework.context.event.EventListener;
 | 
					import org.springframework.context.event.EventListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
 | 
					import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
 | 
				
			||||||
| 
						 | 
					@ -76,6 +78,9 @@ public interface ScreenProctoringService extends SessionUpdateTask {
 | 
				
			||||||
    @EventListener(ExamDeletionEvent.class)
 | 
					    @EventListener(ExamDeletionEvent.class)
 | 
				
			||||||
    void notifyExamDeletion(final ExamDeletionEvent event);
 | 
					    void notifyExamDeletion(final ExamDeletionEvent event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @EventListener
 | 
				
			||||||
 | 
					    void notifyLmsSetupChange(final LmsSetupChangeEvent event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** This is used to update the exam equivalent on the screen proctoring service side
 | 
					    /** This is used to update the exam equivalent on the screen proctoring service side
 | 
				
			||||||
     * if screen proctoring is enabled for the specified exam.
 | 
					     * if screen proctoring is enabled for the specified exam.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
| 
						 | 
					@ -93,6 +98,7 @@ public interface ScreenProctoringService extends SessionUpdateTask {
 | 
				
			||||||
    @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
 | 
					    @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
 | 
				
			||||||
    void synchronizeSPSUser(final String userUUID);
 | 
					    void synchronizeSPSUser(final String userUUID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
 | 
					    @Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
 | 
				
			||||||
    void synchronizeSPSUserForExam(final Long examId);
 | 
					    void synchronizeSPSUserForExam(final Long examId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,9 +10,11 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.gbl.model.Page;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
 | 
					import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
 | 
					import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
 | 
					import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
 | 
				
			||||||
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;
 | 
				
			||||||
| 
						 | 
					@ -111,6 +113,7 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /** The screen proctoring service group API attribute names */
 | 
					        /** The screen proctoring service group API attribute names */
 | 
				
			||||||
        interface EXAM {
 | 
					        interface EXAM {
 | 
				
			||||||
 | 
					            String ATTR_ID = "id";
 | 
				
			||||||
            String ATTR_UUID = "uuid";
 | 
					            String ATTR_UUID = "uuid";
 | 
				
			||||||
            String ATTR_SEB_SERVER_ID = "sebserverId";
 | 
					            String ATTR_SEB_SERVER_ID = "sebserverId";
 | 
				
			||||||
            String ATTR_NAME = "name";
 | 
					            String ATTR_NAME = "name";
 | 
				
			||||||
| 
						 | 
					@ -269,7 +272,7 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
                    SPSData.class);
 | 
					                    SPSData.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } catch (final Exception e) {
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
            log.error("Failed to get local SPSData for exam: {}", examId);
 | 
					            log.warn("Failed to get local SPSData for exam: {}", examId);
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -291,6 +294,8 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // if we have an exam where SPS was initialized before but deactivated meanwhile
 | 
				
			||||||
 | 
					            // reactivate on SPS site and synchronize
 | 
				
			||||||
            if (exam.additionalAttributes.containsKey(SPSData.ATTR_SPS_ACTIVE)) {
 | 
					            if (exam.additionalAttributes.containsKey(SPSData.ATTR_SPS_ACTIVE)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                log.info("SPS Exam for SEB Server Exam: {} already exists. Try to re-activate", exam.externalId);
 | 
					                log.info("SPS Exam for SEB Server Exam: {} already exists. Try to re-activate", exam.externalId);
 | 
				
			||||||
| 
						 | 
					@ -298,51 +303,55 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
                final SPSData spsData = this.getSPSData(exam.id);
 | 
					                final SPSData spsData = this.getSPSData(exam.id);
 | 
				
			||||||
                // re-activate all needed entities on SPS side
 | 
					                // re-activate all needed entities on SPS side
 | 
				
			||||||
                if (exam.status == Exam.ExamStatus.RUNNING) {
 | 
					                if (exam.status == Exam.ExamStatus.RUNNING) {
 | 
				
			||||||
                    activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, true, apiTemplate);
 | 
					                    activateScreenProctoring(exam).getOrThrow();
 | 
				
			||||||
                    activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, true, apiTemplate);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                synchronizeUserAccounts(exam);
 | 
					                synchronizeUserAccounts(exam);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                // mark successfully activated on SPS side
 | 
					 | 
				
			||||||
                this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
					 | 
				
			||||||
                        EntityType.EXAM,
 | 
					 | 
				
			||||||
                        exam.id,
 | 
					 | 
				
			||||||
                        SPSData.ATTR_SPS_ACTIVE,
 | 
					 | 
				
			||||||
                        Constants.TRUE_STRING);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return Collections.emptyList();
 | 
					                return Collections.emptyList();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final SPSData spsData = new SPSData();
 | 
					            // if we have a new Exam but Exam on SPS site for ExamUUID exists, reinitialize the exam and synchronize
 | 
				
			||||||
            log.info(
 | 
					            if (existsExamOnSPS(exam)) {
 | 
				
			||||||
                    "SPS Exam for SEB Server Exam: {} don't exists yet, create necessary structures on SPS",
 | 
					                return reinitializeScreenProctoring(exam);
 | 
				
			||||||
                    exam.externalId);
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            synchronizeUserAccounts(exam);
 | 
					            // If this is a completely new exam with new SPS binding, initialize it
 | 
				
			||||||
            createSEBAccess(exam, apiTemplate, spsData);
 | 
					            return initializeScreenProctoring(exam, apiTemplate);
 | 
				
			||||||
            createExam(exam, apiTemplate, spsData);
 | 
					 | 
				
			||||||
            final Collection<ScreenProctoringGroup> initializeGroups = initializeGroups(exam, apiTemplate, spsData);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // store encrypted spsData
 | 
					 | 
				
			||||||
            final String spsDataJSON = this.jsonMapper.writeValueAsString(spsData);
 | 
					 | 
				
			||||||
            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
					 | 
				
			||||||
                    EntityType.EXAM,
 | 
					 | 
				
			||||||
                    exam.id,
 | 
					 | 
				
			||||||
                    SPSData.ATTR_SPS_ACCESS_DATA,
 | 
					 | 
				
			||||||
                    this.cryptor.encrypt(spsDataJSON).getOrThrow().toString());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // mark successfully activated on SPS side
 | 
					 | 
				
			||||||
            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
					 | 
				
			||||||
                    EntityType.EXAM,
 | 
					 | 
				
			||||||
                    exam.id,
 | 
					 | 
				
			||||||
                    SPSData.ATTR_SPS_ACTIVE,
 | 
					 | 
				
			||||||
                    Constants.TRUE_STRING);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return initializeGroups;
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean existsExamOnSPS(final Exam exam) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
				
			||||||
 | 
					            final String uri = UriComponentsBuilder
 | 
				
			||||||
 | 
					                    .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
 | 
				
			||||||
 | 
					                    .path(SPS_API.EXAM_ENDPOINT)
 | 
				
			||||||
 | 
					                    .pathSegment(createExamUUID(exam))
 | 
				
			||||||
 | 
					                    .build().toUriString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.GET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (exchange.getStatusCode() != HttpStatus.NOT_FOUND) {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            } else if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                log.warn("Failed to verify if Exam on SPS already exists: {}", exchange.getBody());
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
 | 
					            log.error("Failed to verify if Exam exists already on SPS site: ", e);
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void synchronizeUserAccount(final String userUUID) {
 | 
					    void synchronizeUserAccount(final String userUUID) {
 | 
				
			||||||
 | 
					        if (UserService.LMS_INTEGRATION_CLIENT_UUID.equals(userUUID)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(null);
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(null);
 | 
				
			||||||
| 
						 | 
					@ -446,22 +455,21 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
                log.error("Failed to update SPS exam data: {}", exchange);
 | 
					                log.error("Failed to update SPS exam data: {}", exchange);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
            log.error("Failed to update exam on SPS service for exam: {}", exam, e);
 | 
					            log.error("Failed to update exam on SPS service for exam: {}", exam, e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** This is called when an exam finishes and deactivates the Exam, SEB Client Access and the ad-hoc User-Account
 | 
					    /** This is called when an exam finishes and deactivates the Exam, SEB Client Access on Screen Proctoring Service side.
 | 
				
			||||||
     * on Screen Proctoring Service side.
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param exam The exam
 | 
					     * @param exam The exam
 | 
				
			||||||
     * @return Result refer to the exam or to an error when happened */
 | 
					     * @return Result refer to the exam or to an error when happened */
 | 
				
			||||||
    Result<Exam> disposeScreenProctoring(final Exam exam) {
 | 
					    Result<Exam> deactivateScreenProctoring(final Exam exam) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Result.tryCatch(() -> {
 | 
					        return Result.tryCatch(() -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (log.isDebugEnabled()) {
 | 
					            if (log.isDebugEnabled()) {
 | 
				
			||||||
                log.debug("Dispose active screen proctoring exam, groups and access on SPS for exam: {}", exam);
 | 
					                log.debug("Deactivate active screen proctoring exam, groups and access on SPS for exam: {}", exam.name);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final SPSData spsData = this.getSPSData(exam.id);
 | 
					            final SPSData spsData = this.getSPSData(exam.id);
 | 
				
			||||||
| 
						 | 
					@ -469,7 +477,7 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
            activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, false, apiTemplate);
 | 
					            activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, false, apiTemplate);
 | 
				
			||||||
            activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, apiTemplate);
 | 
					            activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, apiTemplate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // mark successfully dispose on SPS side
 | 
					            // mark local for successfully dispose on SPS side
 | 
				
			||||||
            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
					            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
                    EntityType.EXAM,
 | 
					                    EntityType.EXAM,
 | 
				
			||||||
                    exam.id,
 | 
					                    exam.id,
 | 
				
			||||||
| 
						 | 
					@ -480,56 +488,197 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** This is called on exam delete and deletes the SEB Client Access and the ad-hoc User-Account
 | 
					    Result<Exam> activateScreenProctoring(final Exam exam) {
 | 
				
			||||||
     * on Screen Proctoring Service side.
 | 
					 | 
				
			||||||
     * Also sends a exam delete request where Exam on SPS gets deleted if there are no session data for the exam
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param exam The exam
 | 
					 | 
				
			||||||
     * @return Result refer to the exam or to an error when happened */
 | 
					 | 
				
			||||||
    Exam deleteScreenProctoring(final Exam exam) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!BooleanUtils.toBoolean(exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) {
 | 
					        return Result.tryCatch(() -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (log.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                log.debug("Activate screen proctoring exam, groups and access on SPS for exam: {}", exam.name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final SPSData spsData = this.getSPSData(exam.id);
 | 
				
			||||||
 | 
					            if (spsData == null) {
 | 
				
			||||||
                return exam;
 | 
					                return exam;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (log.isDebugEnabled()) {
 | 
					 | 
				
			||||||
                log.debug("Deactivate exam and groups on SPS site and send deletion request for exam {}", exam);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
				
			||||||
            final SPSData spsData = this.getSPSData(exam.id);
 | 
					            activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, true, apiTemplate);
 | 
				
			||||||
            deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, apiTemplate);
 | 
					            activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, true, apiTemplate);
 | 
				
			||||||
            activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, apiTemplate);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // exam delete request on SPS
 | 
					            // mark local for successfully activated on SPS side
 | 
				
			||||||
            final String uri = UriComponentsBuilder
 | 
					 | 
				
			||||||
                    .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
 | 
					 | 
				
			||||||
                    .path(SPS_API.EXAM_ENDPOINT)
 | 
					 | 
				
			||||||
                    .pathSegment(spsData.spsExamUUID)
 | 
					 | 
				
			||||||
                    .pathSegment(SPS_API.EXAM_DELETE_REQUEST_ENDPOINT)
 | 
					 | 
				
			||||||
                    .build()
 | 
					 | 
				
			||||||
                    .toUriString();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.DELETE);
 | 
					 | 
				
			||||||
            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
					 | 
				
			||||||
                log.error("Failed to request delete on SPS for Exam: {} with response: {}", exam, exchange);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // mark successfully dispose on SPS side
 | 
					 | 
				
			||||||
            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
					            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
                    EntityType.EXAM,
 | 
					                    EntityType.EXAM,
 | 
				
			||||||
                    exam.id,
 | 
					                    exam.id,
 | 
				
			||||||
                    SPSData.ATTR_SPS_ACTIVE,
 | 
					                    SPSData.ATTR_SPS_ACTIVE,
 | 
				
			||||||
                    Constants.FALSE_STRING);
 | 
					                    Constants.TRUE_STRING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return exam;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Collection<ScreenProctoringGroup> initializeScreenProctoring(
 | 
				
			||||||
 | 
					            final Exam exam,
 | 
				
			||||||
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate) throws JsonProcessingException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final SPSData spsData = new SPSData();
 | 
				
			||||||
 | 
					        log.info(
 | 
				
			||||||
 | 
					                "SPS Exam for SEB Server Exam: {} don't exists yet, create necessary structures on SPS",
 | 
				
			||||||
 | 
					                exam.externalId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronizeUserAccounts(exam);
 | 
				
			||||||
 | 
					        createSEBAccess(exam, apiTemplate, spsData);
 | 
				
			||||||
 | 
					        createExam(exam, apiTemplate, spsData);
 | 
				
			||||||
 | 
					        final Collection<ScreenProctoringGroup> initializeGroups = initializeGroups(exam, apiTemplate, spsData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // store encrypted spsData
 | 
				
			||||||
 | 
					        final String spsDataJSON = this.jsonMapper.writeValueAsString(spsData);
 | 
				
			||||||
 | 
					        this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
 | 
					                EntityType.EXAM,
 | 
				
			||||||
 | 
					                exam.id,
 | 
				
			||||||
 | 
					                SPSData.ATTR_SPS_ACCESS_DATA,
 | 
				
			||||||
 | 
					                this.cryptor.encrypt(spsDataJSON).getOrThrow().toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // mark successfully activated on SPS side
 | 
				
			||||||
 | 
					        this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
 | 
					                EntityType.EXAM,
 | 
				
			||||||
 | 
					                exam.id,
 | 
				
			||||||
 | 
					                SPSData.ATTR_SPS_ACTIVE,
 | 
				
			||||||
 | 
					                Constants.TRUE_STRING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return initializeGroups;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Collection<ScreenProctoringGroup> reinitializeScreenProctoring(final Exam exam) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // get exam from SPS
 | 
				
			||||||
 | 
					            final String examUUID = createExamUUID(exam);
 | 
				
			||||||
 | 
					            final String uri = UriComponentsBuilder
 | 
				
			||||||
 | 
					                    .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
 | 
				
			||||||
 | 
					                    .path(SPS_API.EXAM_ENDPOINT)
 | 
				
			||||||
 | 
					                    .pathSegment(examUUID)
 | 
				
			||||||
 | 
					                    .build().toUriString();
 | 
				
			||||||
 | 
					            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.GET);
 | 
				
			||||||
 | 
					            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
 | 
					                throw new RuntimeException("Failed to get Exam from SPS. local exam uuid: " + examUUID);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            final JsonNode requestJSON = this.jsonMapper.readTree(exchange.getBody());
 | 
				
			||||||
 | 
					            final String spsExamId = requestJSON.get(SPS_API.EXAM.ATTR_ID).textValue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // check if Exam has SPSData, if not create and if check completeness
 | 
				
			||||||
 | 
					            SPSData spsData = this.getSPSData(exam.id);
 | 
				
			||||||
 | 
					            if (spsData == null) {
 | 
				
			||||||
 | 
					                spsData = new SPSData();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // create new SEB Account on SPS if needed
 | 
				
			||||||
 | 
					            if (spsData.spsSEBAccessUUID == null) {
 | 
				
			||||||
 | 
					                createSEBAccess(exam, apiTemplate, spsData);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            spsData.spsExamUUID = examUUID;
 | 
				
			||||||
 | 
					            // store encrypted spsData
 | 
				
			||||||
 | 
					            final String spsDataJSON = this.jsonMapper.writeValueAsString(spsData);
 | 
				
			||||||
 | 
					            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
 | 
					                    EntityType.EXAM,
 | 
				
			||||||
 | 
					                    exam.id,
 | 
				
			||||||
 | 
					                    SPSData.ATTR_SPS_ACCESS_DATA,
 | 
				
			||||||
 | 
					                    this.cryptor.encrypt(spsDataJSON).getOrThrow().toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // reactivate exam on SPS
 | 
				
			||||||
 | 
					            this.activateScreenProctoring(exam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // recreate groups on SEB Server if needed
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                final Collection<ScreenProctoringGroup> groups = new ArrayList<>();
 | 
				
			||||||
 | 
					                final String groupRequestURI = UriComponentsBuilder
 | 
				
			||||||
 | 
					                        .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
 | 
				
			||||||
 | 
					                        .path(SPS_API.GROUP_ENDPOINT)
 | 
				
			||||||
 | 
					                        .queryParam(Page.ATTR_PAGE_SIZE, 100)
 | 
				
			||||||
 | 
					                        .queryParam(SPS_API.GROUP.ATTR_EXAM_ID, spsExamId)
 | 
				
			||||||
 | 
					                        .build()
 | 
				
			||||||
 | 
					                        .toUriString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                final JsonNode groupsJSON = this.jsonMapper.readTree(exchange.getBody());
 | 
				
			||||||
 | 
					                final JsonNode pageContent = groupsJSON.get("content");
 | 
				
			||||||
 | 
					                if (pageContent.isArray()) {
 | 
				
			||||||
 | 
					                   for (final JsonNode group : pageContent) {
 | 
				
			||||||
 | 
					                       groups.add(new ScreenProctoringGroup(
 | 
				
			||||||
 | 
					                               null,
 | 
				
			||||||
 | 
					                               exam.id,
 | 
				
			||||||
 | 
					                               group.get(SPS_API.GROUP.ATTR_UUID).textValue(),
 | 
				
			||||||
 | 
					                               group.get(SPS_API.GROUP.ATTR_NAME).textValue(),
 | 
				
			||||||
 | 
					                               0,
 | 
				
			||||||
 | 
					                               null
 | 
				
			||||||
 | 
					                       ));
 | 
				
			||||||
 | 
					                   }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return groups;
 | 
				
			||||||
 | 
					            } catch (final Exception e) {
 | 
				
			||||||
 | 
					                log.error("Failed to get exam groups from SPS due to reinitialization: ", e);
 | 
				
			||||||
 | 
					                return initializeGroups(exam, apiTemplate, spsData);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } catch (final Exception e) {
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
            log.warn("Failed to apply SPS deletion of exam: {} error: {}", exam, e.getMessage());
 | 
					            log.error("Failed to re-initialize Screen Proctoring: ", e);
 | 
				
			||||||
 | 
					            return Collections.emptyList();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return exam;
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    /** This is called on exam delete and deletes the SEB Client Access and the ad-hoc User-Account
 | 
				
			||||||
 | 
					//     * on Screen Proctoring Service side.
 | 
				
			||||||
 | 
					//     * Also sends a exam delete request where Exam on SPS gets deleted if there are no session data for the exam
 | 
				
			||||||
 | 
					//     *
 | 
				
			||||||
 | 
					//     * @param exam The exam
 | 
				
			||||||
 | 
					//     * @return Result refer to the exam or to an error when happened */
 | 
				
			||||||
 | 
					//    Exam deleteScreenProctoring(final Exam exam) {
 | 
				
			||||||
 | 
					//        try {
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//            if (!BooleanUtils.toBoolean(exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) {
 | 
				
			||||||
 | 
					//                return exam;
 | 
				
			||||||
 | 
					//            }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//            if (log.isDebugEnabled()) {
 | 
				
			||||||
 | 
					//                log.debug("Deactivate exam and groups on SPS site and send deletion request for exam {}", exam);
 | 
				
			||||||
 | 
					//            }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
				
			||||||
 | 
					//            final SPSData spsData = this.getSPSData(exam.id);
 | 
				
			||||||
 | 
					//            deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, apiTemplate);
 | 
				
			||||||
 | 
					//            activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, apiTemplate);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//            // exam delete request on SPS
 | 
				
			||||||
 | 
					//            final String uri = UriComponentsBuilder
 | 
				
			||||||
 | 
					//                    .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
 | 
				
			||||||
 | 
					//                    .path(SPS_API.EXAM_ENDPOINT)
 | 
				
			||||||
 | 
					//                    .pathSegment(spsData.spsExamUUID)
 | 
				
			||||||
 | 
					//                    .pathSegment(SPS_API.EXAM_DELETE_REQUEST_ENDPOINT)
 | 
				
			||||||
 | 
					//                    .build()
 | 
				
			||||||
 | 
					//                    .toUriString();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.DELETE);
 | 
				
			||||||
 | 
					//            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
 | 
					//                log.error("Failed to request delete on SPS for Exam: {} with response: {}", exam, exchange);
 | 
				
			||||||
 | 
					//            }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//            // mark successfully dispose on SPS side
 | 
				
			||||||
 | 
					//            this.additionalAttributesDAO.saveAdditionalAttribute(
 | 
				
			||||||
 | 
					//                    EntityType.EXAM,
 | 
				
			||||||
 | 
					//                    exam.id,
 | 
				
			||||||
 | 
					//                    SPSData.ATTR_SPS_ACTIVE,
 | 
				
			||||||
 | 
					//                    Constants.FALSE_STRING);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//        } catch (final Exception e) {
 | 
				
			||||||
 | 
					//            log.warn("Failed to apply SPS deletion of exam: {} error: {}", exam, e.getMessage());
 | 
				
			||||||
 | 
					//        }
 | 
				
			||||||
 | 
					//        return exam;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Result<ScreenProctoringGroup> createGroup(
 | 
					    Result<ScreenProctoringGroup> createGroup(
 | 
				
			||||||
            final String spsExamUUID,
 | 
					            final String spsExamUUID,
 | 
				
			||||||
            final int groupNumber,
 | 
					            final int groupNumber,
 | 
				
			||||||
| 
						 | 
					@ -583,7 +732,7 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        params.add(SPS_API.SESSION.ATTR_CLIENT_VERSION, clientConnection.getClientVersion());
 | 
					        params.add(SPS_API.SESSION.ATTR_CLIENT_VERSION, clientConnection.getClientVersion());
 | 
				
			||||||
        final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
					        final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded, HttpMethod.POST);
 | 
					        final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded);
 | 
				
			||||||
        if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
					        if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
            throw new RuntimeException(
 | 
					            throw new RuntimeException(
 | 
				
			||||||
                    "Failed to create SPS SEB session for SEB connection: " + token);
 | 
					                    "Failed to create SPS SEB session for SEB connection: " + token);
 | 
				
			||||||
| 
						 | 
					@ -592,17 +741,18 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        return token;
 | 
					        return token;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void activateSEBAccessOnSPS(final Exam exam, final boolean activate) {
 | 
					//    void activateExamOnSPS(final Exam exam, final boolean activate) {
 | 
				
			||||||
        try {
 | 
					//        try {
 | 
				
			||||||
            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
					//            final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
 | 
				
			||||||
            final SPSData spsData = this.getSPSData(exam.id);
 | 
					//            final SPSData spsData = this.getSPSData(exam.id);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
            activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, activate, apiTemplate);
 | 
					//            activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsSEBAccessUUID, activate, apiTemplate);
 | 
				
			||||||
 | 
					//            activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, activate, apiTemplate);
 | 
				
			||||||
        } catch (final Exception e) {
 | 
					//
 | 
				
			||||||
            log.error("Failed to de/activate SEB Access on SPS for exam: {}", exam);
 | 
					//        } catch (final Exception e) {
 | 
				
			||||||
        }
 | 
					//            log.error("Failed to de/activate SEB Access on SPS for exam: {}", exam);
 | 
				
			||||||
    }
 | 
					//        }
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void synchronizeUserAccount(
 | 
					    private void synchronizeUserAccount(
 | 
				
			||||||
            final String userUUID,
 | 
					            final String userUUID,
 | 
				
			||||||
| 
						 | 
					@ -758,17 +908,17 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        params.add(SPS_API.GROUP.ATTR_EXAM_ID, spsExamUUID);
 | 
					        params.add(SPS_API.GROUP.ATTR_EXAM_ID, spsExamUUID);
 | 
				
			||||||
        final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
					        final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded, HttpMethod.POST);
 | 
					        final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded);
 | 
				
			||||||
        if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
					        if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
            throw new RuntimeException("Failed to create SPS SEB group for exam: " + spsExamUUID);
 | 
					            throw new RuntimeException("Failed to create SPS SEB group for exam: " + spsExamUUID);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final Map<String, String> userAttributes = this.jsonMapper.readValue(
 | 
					        final Map<String, String> groupAttributes = this.jsonMapper.readValue(
 | 
				
			||||||
                exchange.getBody(),
 | 
					                exchange.getBody(),
 | 
				
			||||||
                new TypeReference<Map<String, String>>() {
 | 
					                new TypeReference<Map<String, String>>() {
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        final String spsGroupUUID = userAttributes.get(SPS_API.GROUP.ATTR_UUID);
 | 
					        final String spsGroupUUID = groupAttributes.get(SPS_API.GROUP.ATTR_UUID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new ScreenProctoringGroup(null, examId, spsGroupUUID, name, size, exchange.getBody());
 | 
					        return new ScreenProctoringGroup(null, examId, spsGroupUUID, name, size, exchange.getBody());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -790,22 +940,11 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
                    .path(SPS_API.EXAM_ENDPOINT)
 | 
					                    .path(SPS_API.EXAM_ENDPOINT)
 | 
				
			||||||
                    .build().toUriString();
 | 
					                    .build().toUriString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
 | 
					            final String uuid = createExamUUID(exam);
 | 
				
			||||||
            params.add(SPS_API.EXAM.ATTR_NAME, exam.name);
 | 
					            final MultiValueMap<String, String> params = createExamCreationParams(exam, uuid, userIds);
 | 
				
			||||||
            params.add(SPS_API.EXAM.ATTR_DESCRIPTION, exam.getDescription());
 | 
					 | 
				
			||||||
            params.add(SPS_API.EXAM.ATTR_URL, exam.getStartURL());
 | 
					 | 
				
			||||||
            if (!userIds.isEmpty()) {
 | 
					 | 
				
			||||||
                params.add(SPS_API.EXAM.ATTR_USER_IDS, StringUtils.join(userIds, Constants.LIST_SEPARATOR));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            params.add(SPS_API.EXAM.ATTR_TYPE, exam.getType().name());
 | 
					 | 
				
			||||||
            params.add(SPS_API.EXAM.ATTR_START_TIME, String.valueOf(exam.startTime.getMillis()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (exam.endTime != null) {
 | 
					 | 
				
			||||||
                params.add(SPS_API.EXAM.ATTR_END_TIME, String.valueOf(exam.endTime.getMillis()));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
					            final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded, HttpMethod.POST);
 | 
					            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded);
 | 
				
			||||||
            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
					            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
                throw new RuntimeException("Error response from Screen Proctoring Service: "
 | 
					                throw new RuntimeException("Error response from Screen Proctoring Service: "
 | 
				
			||||||
                        + exchange.getStatusCodeValue()
 | 
					                        + exchange.getStatusCodeValue()
 | 
				
			||||||
| 
						 | 
					@ -814,7 +953,14 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final JsonNode requestJSON = this.jsonMapper.readTree(exchange.getBody());
 | 
					            final JsonNode requestJSON = this.jsonMapper.readTree(exchange.getBody());
 | 
				
			||||||
            spsData.spsExamUUID = requestJSON.get(SPS_API.EXAM.ATTR_UUID).textValue();
 | 
					            final String respondedUUID = requestJSON.get(SPS_API.EXAM.ATTR_UUID).textValue();
 | 
				
			||||||
 | 
					            if (!uuid.equals(respondedUUID)) {
 | 
				
			||||||
 | 
					                log.warn("Detected Exam ({}) generation UUID mismatch. propagated UUID: {} responded UUID: {}",
 | 
				
			||||||
 | 
					                        exam.name,
 | 
				
			||||||
 | 
					                        uuid,
 | 
				
			||||||
 | 
					                        respondedUUID);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            spsData.spsExamUUID = respondedUUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } catch (final Exception e) {
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
            log.error(
 | 
					            log.error(
 | 
				
			||||||
| 
						 | 
					@ -826,6 +972,32 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static MultiValueMap<String, String> createExamCreationParams(
 | 
				
			||||||
 | 
					            final Exam exam,
 | 
				
			||||||
 | 
					            final String uuid,
 | 
				
			||||||
 | 
					            final List<String> userIds) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
 | 
				
			||||||
 | 
					        params.add(SPS_API.EXAM.ATTR_UUID, uuid);
 | 
				
			||||||
 | 
					        params.add(SPS_API.EXAM.ATTR_NAME, exam.name);
 | 
				
			||||||
 | 
					        params.add(SPS_API.EXAM.ATTR_DESCRIPTION, exam.getDescription());
 | 
				
			||||||
 | 
					        params.add(SPS_API.EXAM.ATTR_URL, exam.getStartURL());
 | 
				
			||||||
 | 
					        if (!userIds.isEmpty()) {
 | 
				
			||||||
 | 
					            params.add(SPS_API.EXAM.ATTR_USER_IDS, StringUtils.join(userIds, Constants.LIST_SEPARATOR));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        params.add(SPS_API.EXAM.ATTR_TYPE, exam.getType().name());
 | 
				
			||||||
 | 
					        params.add(SPS_API.EXAM.ATTR_START_TIME, String.valueOf(exam.startTime.getMillis()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (exam.endTime != null) {
 | 
				
			||||||
 | 
					            params.add(SPS_API.EXAM.ATTR_END_TIME, String.valueOf(exam.endTime.getMillis()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return params;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String createExamUUID(final Exam exam) {
 | 
				
			||||||
 | 
					        return exam.externalId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void createSEBAccess(
 | 
					    private void createSEBAccess(
 | 
				
			||||||
            final Exam exam,
 | 
					            final Exam exam,
 | 
				
			||||||
            final ScreenProctoringServiceOAuthTemplate apiTemplate,
 | 
					            final ScreenProctoringServiceOAuthTemplate apiTemplate,
 | 
				
			||||||
| 
						 | 
					@ -846,7 +1018,7 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
            params.add(SPS_API.SEB_ACCESS.ATTR_DESCRIPTION, description);
 | 
					            params.add(SPS_API.SEB_ACCESS.ATTR_DESCRIPTION, description);
 | 
				
			||||||
            final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
					            final String paramsFormEncoded = Utils.toAppFormUrlEncodedBody(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded, HttpMethod.POST);
 | 
					            final ResponseEntity<String> exchange = apiTemplate.exchange(uri, paramsFormEncoded);
 | 
				
			||||||
            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
					            if (exchange.getStatusCode() != HttpStatus.OK) {
 | 
				
			||||||
                throw new RuntimeException("Failed to create SPS SEB access for exam: " + exam.externalId);
 | 
					                throw new RuntimeException("Failed to create SPS SEB access for exam: " + exam.externalId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -1099,10 +1271,9 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ResponseEntity<String> exchange(
 | 
					        ResponseEntity<String> exchange(
 | 
				
			||||||
                final String url,
 | 
					                final String url,
 | 
				
			||||||
                final String body,
 | 
					                final String body) {
 | 
				
			||||||
                final HttpMethod method) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return exchange(url, method, body, getHeaders());
 | 
					            return exchange(url, HttpMethod.POST, body, getHeaders());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HttpHeaders getHeadersJSONRequest() {
 | 
					        HttpHeaders getHeadersJSONRequest() {
 | 
				
			||||||
| 
						 | 
					@ -1160,8 +1331,6 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        public static final String ATTR_SPS_ACTIVE = "spsExamActive";
 | 
					        public static final String ATTR_SPS_ACTIVE = "spsExamActive";
 | 
				
			||||||
        public static final String ATTR_SPS_ACCESS_DATA = "spsAccessData";
 | 
					        public static final String ATTR_SPS_ACCESS_DATA = "spsAccessData";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @JsonProperty("spsUserPWD")
 | 
					 | 
				
			||||||
        String spsUserPWD = null;
 | 
					 | 
				
			||||||
        @JsonProperty("spsSEBAccessUUID")
 | 
					        @JsonProperty("spsSEBAccessUUID")
 | 
				
			||||||
        String spsSEBAccessUUID = null;
 | 
					        String spsSEBAccessUUID = null;
 | 
				
			||||||
        @JsonProperty("spsSEBAccessName")
 | 
					        @JsonProperty("spsSEBAccessName")
 | 
				
			||||||
| 
						 | 
					@ -1175,16 +1344,13 @@ class ScreenProctoringAPIBinding {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @JsonCreator
 | 
					        @JsonCreator
 | 
				
			||||||
        public SPSData(
 | 
					        public SPSData(@JsonProperty("spsSEBAccessUUID") final String spsSEBAccessUUID,
 | 
				
			||||||
                @JsonProperty("spsUserPWD") final String spsUserPWD,
 | 
					 | 
				
			||||||
                @JsonProperty("spsSEBAccessUUID") final String spsSEBAccessUUID,
 | 
					 | 
				
			||||||
                // NOTE: this is only for compatibility reasons, TODO as soon as possible
 | 
					                // NOTE: this is only for compatibility reasons, TODO as soon as possible
 | 
				
			||||||
                @JsonProperty("spsSEBAccesUUID") final String spsSEBAccesUUID,
 | 
					                @JsonProperty("spsSEBAccesUUID") final String spsSEBAccesUUID,
 | 
				
			||||||
                @JsonProperty("spsSEBAccessName") final String spsSEBAccessName,
 | 
					                @JsonProperty("spsSEBAccessName") final String spsSEBAccessName,
 | 
				
			||||||
                @JsonProperty("spsSEBAccessPWD") final String spsSEBAccessPWD,
 | 
					                @JsonProperty("spsSEBAccessPWD") final String spsSEBAccessPWD,
 | 
				
			||||||
                @JsonProperty("psExamUUID") final String spsExamUUID) {
 | 
					                @JsonProperty("psExamUUID") final String spsExamUUID) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.spsUserPWD = spsUserPWD;
 | 
					 | 
				
			||||||
            this.spsSEBAccessUUID = StringUtils.isNotBlank(spsSEBAccesUUID) ? spsSEBAccesUUID : spsSEBAccessUUID;
 | 
					            this.spsSEBAccessUUID = StringUtils.isNotBlank(spsSEBAccesUUID) ? spsSEBAccesUUID : spsSEBAccessUUID;
 | 
				
			||||||
            this.spsSEBAccessName = spsSEBAccessName;
 | 
					            this.spsSEBAccessName = spsSEBAccessName;
 | 
				
			||||||
            this.spsSEBAccessPWD = spsSEBAccessPWD;
 | 
					            this.spsSEBAccessPWD = spsSEBAccessPWD;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,14 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_SCREEN_PROCTORING.*;
 | 
					import static ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_SCREEN_PROCTORING.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
 | 
					import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
 | 
				
			||||||
 | 
					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 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;
 | 
				
			||||||
| 
						 | 
					@ -184,7 +183,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
 | 
				
			||||||
                    final boolean isEnabling = this.proctoringSettingsDAO.isScreenProctoringEnabled(exam.id);
 | 
					                    final boolean isEnabling = this.proctoringSettingsDAO.isScreenProctoringEnabled(exam.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (isEnabling && !isSPSActive) {
 | 
					                    if (isEnabling && !isSPSActive) {
 | 
				
			||||||
 | 
					                        // if screen proctoring has been enabled
 | 
				
			||||||
                        this.screenProctoringAPIBinding
 | 
					                        this.screenProctoringAPIBinding
 | 
				
			||||||
                                .startScreenProctoring(exam)
 | 
					                                .startScreenProctoring(exam)
 | 
				
			||||||
                                .onError(error -> log.error(
 | 
					                                .onError(error -> log.error(
 | 
				
			||||||
| 
						 | 
					@ -197,9 +196,9 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
 | 
				
			||||||
                        this.examDAO.markUpdate(exam.id);
 | 
					                        this.examDAO.markUpdate(exam.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    } else if (!isEnabling && isSPSActive) {
 | 
					                    } else if (!isEnabling && isSPSActive) {
 | 
				
			||||||
 | 
					                        // if screen proctoring has been disabled...
 | 
				
			||||||
                        this.screenProctoringAPIBinding
 | 
					                        this.screenProctoringAPIBinding
 | 
				
			||||||
                                .disposeScreenProctoring(exam)
 | 
					                                .deactivateScreenProctoring(exam)
 | 
				
			||||||
                                .onError(error -> log.error("Failed to dispose screen proctoring for exam: {}",
 | 
					                                .onError(error -> log.error("Failed to dispose screen proctoring for exam: {}",
 | 
				
			||||||
                                        exam,
 | 
					                                        exam,
 | 
				
			||||||
                                        error))
 | 
					                                        error))
 | 
				
			||||||
| 
						 | 
					@ -301,7 +300,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.screenProctoringAPIBinding.activateSEBAccessOnSPS(exam, true);
 | 
					        this.screenProctoringAPIBinding.activateScreenProctoring(exam);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
| 
						 | 
					@ -311,7 +310,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.screenProctoringAPIBinding.activateSEBAccessOnSPS(exam, false);
 | 
					        this.screenProctoringAPIBinding.deactivateScreenProctoring(exam);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
| 
						 | 
					@ -330,6 +329,37 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void notifyLmsSetupChange(final LmsSetupChangeEvent event) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (event.activation == Activatable.ActivationAction.NONE) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            examDAO.allActiveForLMSSetup(Arrays.asList(event.getLmsSetup().id))
 | 
				
			||||||
 | 
					                    .getOrThrow()
 | 
				
			||||||
 | 
					                    .forEach(exam -> {
 | 
				
			||||||
 | 
					                        if (screenProctoringAPIBinding.isSPSActive(exam)) {
 | 
				
			||||||
 | 
					                            if (event.activation == Activatable.ActivationAction.ACTIVATE) {
 | 
				
			||||||
 | 
					                                this.screenProctoringAPIBinding.activateScreenProctoring(exam)
 | 
				
			||||||
 | 
					                                        .onError(error -> log.warn("Failed to re-activate SPS for exam: {} error: {}",
 | 
				
			||||||
 | 
					                                                exam.name,
 | 
				
			||||||
 | 
					                                                error.getMessage()));
 | 
				
			||||||
 | 
					                            } else if (event.activation == Activatable.ActivationAction.DEACTIVATE) {
 | 
				
			||||||
 | 
					                                this.screenProctoringAPIBinding.deactivateScreenProctoring(exam)
 | 
				
			||||||
 | 
					                                        .onError(error -> log.warn("Failed to deactivate SPS for exam: {} error: {}",
 | 
				
			||||||
 | 
					                                                exam.name,
 | 
				
			||||||
 | 
					                                                error.getMessage()));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (final Exception e) {
 | 
				
			||||||
 | 
					            log.error("Failed to apply LMSSetup change activation/deactivation to Screen Proctoring: ", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void applyScreenProctoringSession(final ClientConnectionRecord ccRecord) {
 | 
					    private void applyScreenProctoringSession(final ClientConnectionRecord ccRecord) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Long placeReservedInGroup = null;
 | 
					        Long placeReservedInGroup = null;
 | 
				
			||||||
| 
						 | 
					@ -457,7 +487,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
 | 
				
			||||||
    private Result<Exam> deleteForExam(final Long examId) {
 | 
					    private Result<Exam> deleteForExam(final Long examId) {
 | 
				
			||||||
        return this.examDAO
 | 
					        return this.examDAO
 | 
				
			||||||
                .byPK(examId)
 | 
					                .byPK(examId)
 | 
				
			||||||
                .map(this.screenProctoringAPIBinding::deleteScreenProctoring)
 | 
					                .flatMap(this.screenProctoringAPIBinding::deactivateScreenProctoring)
 | 
				
			||||||
                .map(this::cleanupAllLocalGroups)
 | 
					                .map(this::cleanupAllLocalGroups)
 | 
				
			||||||
                .onError(error -> log.error("Failed to delete SPS integration for exam: {}", examId, error));
 | 
					                .onError(error -> log.error("Failed to delete SPS integration for exam: {}", examId, error));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.validation.Valid;
 | 
					import javax.validation.Valid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.gbl.model.Activatable;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
 | 
					import ch.ethz.seb.sebserver.gbl.util.Cryptor;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamImportService;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamImportService;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamUtils;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamUtils;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import ch.ethz.seb.sebserver.gbl.model.Activatable;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsTestService;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsTestService;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
 | 
				
			||||||
import org.mybatis.dynamic.sql.SqlTable;
 | 
					import org.mybatis.dynamic.sql.SqlTable;
 | 
				
			||||||
import org.slf4j.Logger;
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
import org.slf4j.LoggerFactory;
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,8 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
 | 
				
			||||||
    private final LmsTestService lmsTestService;
 | 
					    private final LmsTestService lmsTestService;
 | 
				
			||||||
    private final SEBRestrictionService sebRestrictionService;
 | 
					    private final SEBRestrictionService sebRestrictionService;
 | 
				
			||||||
    private final FullLmsIntegrationService fullLmsIntegrationService;
 | 
					    private final FullLmsIntegrationService fullLmsIntegrationService;
 | 
				
			||||||
    final ApplicationEventPublisher applicationEventPublisher;
 | 
					    private final ApplicationEventPublisher applicationEventPublisher;
 | 
				
			||||||
 | 
					    private final ScreenProctoringService screenProctoringService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public LmsSetupController(
 | 
					    public LmsSetupController(
 | 
				
			||||||
            final LmsSetupDAO lmsSetupDAO,
 | 
					            final LmsSetupDAO lmsSetupDAO,
 | 
				
			||||||
| 
						 | 
					@ -74,7 +76,8 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
 | 
				
			||||||
            final LmsTestService lmsTestService,
 | 
					            final LmsTestService lmsTestService,
 | 
				
			||||||
            final SEBRestrictionService sebRestrictionService,
 | 
					            final SEBRestrictionService sebRestrictionService,
 | 
				
			||||||
            final FullLmsIntegrationService fullLmsIntegrationService,
 | 
					            final FullLmsIntegrationService fullLmsIntegrationService,
 | 
				
			||||||
            final ApplicationEventPublisher applicationEventPublisher) {
 | 
					            final ApplicationEventPublisher applicationEventPublisher,
 | 
				
			||||||
 | 
					            final ScreenProctoringService screenProctoringService) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super(authorization,
 | 
					        super(authorization,
 | 
				
			||||||
                bulkActionService,
 | 
					                bulkActionService,
 | 
				
			||||||
| 
						 | 
					@ -88,6 +91,7 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
 | 
				
			||||||
        this.sebRestrictionService = sebRestrictionService;
 | 
					        this.sebRestrictionService = sebRestrictionService;
 | 
				
			||||||
        this.fullLmsIntegrationService = fullLmsIntegrationService;
 | 
					        this.fullLmsIntegrationService = fullLmsIntegrationService;
 | 
				
			||||||
        this.applicationEventPublisher = applicationEventPublisher;
 | 
					        this.applicationEventPublisher = applicationEventPublisher;
 | 
				
			||||||
 | 
					        this.screenProctoringService = screenProctoringService;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
| 
						 | 
					@ -166,20 +170,35 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
 | 
				
			||||||
        return super.notifySaved(entity, activation);
 | 
					        return super.notifySaved(entity, activation);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					//    @Override
 | 
				
			||||||
    protected Result<LmsSetup> validForActivation(final LmsSetup entity, final boolean activation) {
 | 
					//    protected Result<LmsSetup> validForActivation(final LmsSetup entity, final boolean activation) {
 | 
				
			||||||
        return super.validForActivation(entity, activation)
 | 
					//        return super.validForActivation(entity, activation)
 | 
				
			||||||
                .map(lmsSetup -> {
 | 
					//                .map(lmsSetup -> {
 | 
				
			||||||
                    if (!activation) {
 | 
					//                    if (!activation) {
 | 
				
			||||||
                        // on deactivation remove all SEB restrictions and delete full integration if in place
 | 
					//                        // on deactivation remove all SEB restrictions and delete full integration if in place
 | 
				
			||||||
                        return sebRestrictionService
 | 
					//                        return sebRestrictionService
 | 
				
			||||||
                                .applyLMSSetupDeactivation(lmsSetup)
 | 
					//                                .applyLMSSetupDeactivation(lmsSetup)
 | 
				
			||||||
                                .flatMap(fullLmsIntegrationService::applyLMSSetupDeactivation)
 | 
					//                                .onErrorDo(error -> {
 | 
				
			||||||
                                .getOrThrow();
 | 
					//                                    log.warn("Failed to apply LMSSetup deactivation for SEB Restriction: ", error);
 | 
				
			||||||
                    }
 | 
					//                                    return lmsSetup;
 | 
				
			||||||
                    return entity;
 | 
					//                                })
 | 
				
			||||||
                });
 | 
					//                                .flatMap(fullLmsIntegrationService::applyLMSSetupDeactivation)
 | 
				
			||||||
    }
 | 
					//                                .onErrorDo(error -> {
 | 
				
			||||||
 | 
					//                                    log.warn("Failed to apply LMSSetup deactivation for LMS full integration: ", error);
 | 
				
			||||||
 | 
					//                                    return lmsSetup;
 | 
				
			||||||
 | 
					//                                })
 | 
				
			||||||
 | 
					//                                .flatMap(screenProctoringService::applyLMSSetupDeactivation)
 | 
				
			||||||
 | 
					//                                .onErrorDo(error -> {
 | 
				
			||||||
 | 
					//                                    log.warn("Failed to apply LMSSetup deactivation for Screen Proctoring: ", error);
 | 
				
			||||||
 | 
					//                                    return lmsSetup;
 | 
				
			||||||
 | 
					//                                })
 | 
				
			||||||
 | 
					//                                .getOrThrow();
 | 
				
			||||||
 | 
					//                    } else {
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//                    }
 | 
				
			||||||
 | 
					//                    return entity;
 | 
				
			||||||
 | 
					//                });
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected Result<LmsSetup> validForDelete(final LmsSetup entity) {
 | 
					    protected Result<LmsSetup> validForDelete(final LmsSetup entity) {
 | 
				
			||||||
| 
						 | 
					@ -187,7 +206,7 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
 | 
				
			||||||
            // if there is a SEB Restriction involved, release all SEB Restriction for exams
 | 
					            // if there is a SEB Restriction involved, release all SEB Restriction for exams
 | 
				
			||||||
            if (entity.lmsType.features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
 | 
					            if (entity.lmsType.features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
 | 
				
			||||||
                sebRestrictionService
 | 
					                sebRestrictionService
 | 
				
			||||||
                        .applyLMSSetupDeactivation(entity)
 | 
					                        .releaseAllRestrictionsOf(entity)
 | 
				
			||||||
                        .getOrThrow();
 | 
					                        .getOrThrow();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // if there is a full LMS integration involved, delete it first on LMS
 | 
					            // if there is a full LMS integration involved, delete it first on LMS
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue