SEBSERV-435 improved SEB Server SPS user account sync
This commit is contained in:
parent
50456b8d9b
commit
6a0d53c8c4
10 changed files with 267 additions and 156 deletions
|
@ -26,7 +26,7 @@ public class AsyncServiceSpringConfig implements AsyncConfigurer {
|
|||
|
||||
public static final String EXECUTOR_BEAN_NAME = "AsyncServiceExecutorBean";
|
||||
|
||||
/** This ThreadPool is used for internal long running background tasks */
|
||||
/** This ThreadPool is used for internal long-running background tasks */
|
||||
@Bean(name = EXECUTOR_BEAN_NAME)
|
||||
public Executor threadPoolTaskExecutor() {
|
||||
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
|
@ -61,8 +61,8 @@ public class AsyncServiceSpringConfig implements AsyncConfigurer {
|
|||
public static final String EXAM_API_PING_SERVICE_EXECUTOR_BEAN_NAME = "examAPIPingThreadPoolTaskExecutor";
|
||||
|
||||
/** This ThreadPool is used for ping handling in a distributed setup and shall reject
|
||||
* incoming ping requests as fast as possible if there is to much load on the DB.
|
||||
* We prefer to loose a shared ping update and respond to the client in time over a client request timeout */
|
||||
* incoming ping requests as fast as possible if there is too much load on the DB.
|
||||
* We prefer to lose a shared ping update and respond to the client in time over a client request timeout */
|
||||
@Bean(name = EXAM_API_PING_SERVICE_EXECUTOR_BEAN_NAME)
|
||||
public Executor examAPIPingThreadPoolTaskExecutor() {
|
||||
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package ch.ethz.seb.sebserver.gbl.model.exam;
|
||||
|
||||
public interface SPSAPIAccessData {
|
||||
|
||||
Long getExamId();
|
||||
|
||||
String getSpsServiceURL();
|
||||
|
||||
String getSpsAPIKey();
|
||||
|
||||
CharSequence getSpsAPISecret();
|
||||
|
||||
String getSpsAccountId();
|
||||
|
||||
CharSequence getSpsAccountPassword();
|
||||
}
|
|
@ -21,7 +21,7 @@ import ch.ethz.seb.sebserver.gbl.Constants;
|
|||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ScreenProctoringSettings {
|
||||
public class ScreenProctoringSettings implements SPSAPIAccessData {
|
||||
|
||||
public static final String ATTR_ENABLE_SCREEN_PROCTORING = "enableScreenProctoring";
|
||||
public static final String ATTR_SPS_SERVICE_URL = "spsServiceURL";
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -329,7 +330,7 @@ public class WebserviceInfo {
|
|||
return builder.toString();
|
||||
}
|
||||
|
||||
public static final class ScreenProctoringServiceBundle {
|
||||
public static final class ScreenProctoringServiceBundle implements SPSAPIAccessData {
|
||||
|
||||
public final boolean bundled;
|
||||
public final String serviceURL;
|
||||
|
@ -362,6 +363,36 @@ public class WebserviceInfo {
|
|||
this.apiAccountPassword = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getExamId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpsServiceURL() {
|
||||
return serviceURL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpsAPIKey() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSpsAPISecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpsAccountId() {
|
||||
return apiAccountName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSpsAccountPassword() {
|
||||
return apiAccountPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -154,7 +154,7 @@ public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
|||
if (parentEntityKey.entityType == EntityType.EXAM) {
|
||||
|
||||
this.screenProctoringService
|
||||
.applyScreenProctoingForExam(settings.examId)
|
||||
.applyScreenProctoringForExam(settings.examId)
|
||||
.onError(error -> this.proctoringSettingsDAO
|
||||
.disableScreenProctoring(screenProctoringSettings.examId))
|
||||
.getOrThrow();
|
||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
|
@ -17,6 +18,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
|
|||
import ch.ethz.seb.sebserver.gbl.model.session.ScreenProctoringGroup;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
public interface ScreenProctoringService extends SessionUpdateTask {
|
||||
|
||||
|
@ -45,7 +47,7 @@ public interface ScreenProctoringService extends SessionUpdateTask {
|
|||
*
|
||||
* @param examId use the screen proctoring settings of the exam with the given exam id
|
||||
* @return Result refer to the given Exam or to an error when happened */
|
||||
Result<Exam> applyScreenProctoingForExam(Long examId);
|
||||
Result<Exam> applyScreenProctoringForExam(Long examId);
|
||||
|
||||
/** Get list of all screen proctoring collecting groups for a particular exam.
|
||||
*
|
||||
|
@ -64,7 +66,7 @@ public interface ScreenProctoringService extends SessionUpdateTask {
|
|||
@EventListener(ExamFinishedEvent.class)
|
||||
void notifyExamFinished(ExamFinishedEvent event);
|
||||
|
||||
/** This is been 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.
|
||||
*
|
||||
* @param event The ExamDeletionEvent reference all PKs of Exams that are going to be deleted. */
|
||||
|
@ -75,8 +77,8 @@ public interface ScreenProctoringService extends SessionUpdateTask {
|
|||
* if screen proctoring is enabled for the specified exam.
|
||||
*
|
||||
* @param examId The SEB Server exam identifier
|
||||
* @return Result refer to the the given exam data or to an error when happened */
|
||||
Result<Exam> updateExamOnScreenProctoingService(Long examId);
|
||||
* @return Result refer to the given exam data or to an error when happened */
|
||||
Result<Exam> updateExamOnScreenProctoringService(Long examId);
|
||||
|
||||
/** This is internally used to update client connections that are active but has no groups assignment yet.
|
||||
* This attaches SEB client connections to proctoring group of an exam in one batch by checking for
|
||||
|
@ -85,4 +87,7 @@ public interface ScreenProctoringService extends SessionUpdateTask {
|
|||
* SPS connection instruction to SEB client to connect and start sending screenshots. */
|
||||
void updateClientConnections();
|
||||
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
void synchronizeSPSUser(final String userUUID);
|
||||
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ class ExamUpdateHandler implements ExamUpdateTask {
|
|||
|
||||
// also update the exam on screen proctoring service if exam has screen proctoring enabled
|
||||
this.screenProctoringService
|
||||
.updateExamOnScreenProctoingService(exam.id)
|
||||
.updateExamOnScreenProctoringService(exam.id)
|
||||
.onError(error -> log
|
||||
.error("Failed to update exam changes for screen proctoring"));
|
||||
|
||||
|
|
|
@ -8,13 +8,11 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SPSAPIAccessData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -70,19 +68,21 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(ScreenProctoringAPIBinding.class);
|
||||
|
||||
//private static final String SEB_SERVER_SCREEN_PROCTORING_USER_PREFIX = "SEBServer_User_";
|
||||
private static final String SEB_SERVER_SCREEN_PROCTORING_SEB_ACCESS_PREFIX = "SEBServer_SEB_Access_";
|
||||
|
||||
static interface SPS_API {
|
||||
|
||||
String PROCTOR_ROLE = "PROCTOR";
|
||||
enum SPSUserRole {
|
||||
ADMIN,
|
||||
PROCTOR
|
||||
}
|
||||
|
||||
String TOKEN_ENDPOINT = "/oauth/token";
|
||||
String TEST_ENDPOINT = "/admin-api/v1/proctoring/group";
|
||||
|
||||
//String USER_ENDPOINT = "/admin-api/v1/useraccount";
|
||||
public static final String USERSYNC_SEBSERVER_ENDPOINT = "/admin-api/v1/useraccount/usersync/sebserver";
|
||||
String ENTIY_PRIVILEGES_ENDPOINT = "/admin-api/v1/useraccount/entityprivilege";
|
||||
String USER_ACCOUNT_ENDPOINT = "/admin-api/v1/useraccount/";
|
||||
String USERSYNC_SEBSERVER_ENDPOINT = USER_ACCOUNT_ENDPOINT + "usersync/sebserver";
|
||||
String ENTITY_PRIVILEGES_ENDPOINT = USER_ACCOUNT_ENDPOINT + "entityprivilege";
|
||||
String EXAM_ENDPOINT = "/admin-api/v1/exam";
|
||||
String SEB_ACCESS_ENDPOINT = "/admin-api/v1/clientaccess";
|
||||
String GROUP_ENDPOINT = "/admin-api/v1/group";
|
||||
|
@ -195,6 +195,7 @@ class ScreenProctoringAPIBinding {
|
|||
private final JSONMapper jsonMapper;
|
||||
private final ProctoringSettingsDAO proctoringSettingsDAO;
|
||||
private final AdditionalAttributesDAO additionalAttributesDAO;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
|
||||
ScreenProctoringAPIBinding(
|
||||
final UserDAO userDAO,
|
||||
|
@ -202,7 +203,8 @@ class ScreenProctoringAPIBinding {
|
|||
final AsyncService asyncService,
|
||||
final JSONMapper jsonMapper,
|
||||
final ProctoringSettingsDAO proctoringSettingsDAO,
|
||||
final AdditionalAttributesDAO additionalAttributesDAO) {
|
||||
final AdditionalAttributesDAO additionalAttributesDAO,
|
||||
final WebserviceInfo webserviceInfo) {
|
||||
|
||||
this.userDAO = userDAO;
|
||||
this.cryptor = cryptor;
|
||||
|
@ -210,14 +212,15 @@ class ScreenProctoringAPIBinding {
|
|||
this.jsonMapper = jsonMapper;
|
||||
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
||||
this.additionalAttributesDAO = additionalAttributesDAO;
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
}
|
||||
|
||||
public Result<Void> testConnection(final ScreenProctoringSettings screenProctoringSettings) {
|
||||
Result<Void> testConnection(final SPSAPIAccessData spsAPIAccessData) {
|
||||
return Result.tryCatch(() -> {
|
||||
try {
|
||||
|
||||
final ScreenProctoringServiceOAuthTemplate newRestTemplate =
|
||||
new ScreenProctoringServiceOAuthTemplate(this, screenProctoringSettings);
|
||||
new ScreenProctoringServiceOAuthTemplate(this, spsAPIAccessData);
|
||||
|
||||
final ResponseEntity<String> result = newRestTemplate.testServiceConnection();
|
||||
|
||||
|
@ -225,7 +228,7 @@ class ScreenProctoringAPIBinding {
|
|||
if (result.getStatusCode().is4xxClientError()) {
|
||||
log.warn(
|
||||
"Failed to establish REST connection to: {}. status: {}",
|
||||
screenProctoringSettings.spsServiceURL, result.getStatusCode());
|
||||
spsAPIAccessData.getSpsServiceURL(), result.getStatusCode());
|
||||
|
||||
throw new FieldValidationException(
|
||||
"serverURL",
|
||||
|
@ -238,7 +241,7 @@ class ScreenProctoringAPIBinding {
|
|||
} catch (final Exception e) {
|
||||
|
||||
log.error("Failed to access SEB Screen Proctoring service at: {}",
|
||||
screenProctoringSettings.spsServiceURL, e);
|
||||
spsAPIAccessData.getSpsServiceURL(), e);
|
||||
throw new FieldValidationException(
|
||||
"serverURL",
|
||||
"proctoringSettings:serverURL:url.noservice");
|
||||
|
@ -246,7 +249,7 @@ class ScreenProctoringAPIBinding {
|
|||
});
|
||||
}
|
||||
|
||||
public boolean isSPSActive(final Exam exam) {
|
||||
boolean isSPSActive(final Exam exam) {
|
||||
try {
|
||||
final String active = this.additionalAttributesDAO
|
||||
.getAdditionalAttribute(
|
||||
|
@ -261,7 +264,7 @@ class ScreenProctoringAPIBinding {
|
|||
}
|
||||
}
|
||||
|
||||
private SPSData getSPSData(final Long examId) {
|
||||
SPSData getSPSData(final Long examId) {
|
||||
try {
|
||||
|
||||
final String dataEncrypted = this.additionalAttributesDAO
|
||||
|
@ -290,7 +293,7 @@ class ScreenProctoringAPIBinding {
|
|||
*
|
||||
* @param exam The exam
|
||||
* @return Result refer to the exam or to an error when happened */
|
||||
public Result<Collection<ScreenProctoringGroup>> startScreenProctoring(final Exam exam) {
|
||||
Result<Collection<ScreenProctoringGroup>> startScreenProctoring(final Exam exam) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
|
@ -298,14 +301,14 @@ class ScreenProctoringAPIBinding {
|
|||
}
|
||||
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
|
||||
|
||||
if (exam.additionalAttributes.containsKey(SPSData.ATTR_SPS_ACTIVE)) {
|
||||
|
||||
log.info("SPS Exam for SEB Server Exam: {} already exists. Try to re-activate", exam.externalId);
|
||||
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
// re-activate all needed entities on SPS side
|
||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, true, apiTemplate);
|
||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, true, apiTemplate);
|
||||
|
||||
// mark successfully activated on SPS side
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
|
@ -323,7 +326,7 @@ class ScreenProctoringAPIBinding {
|
|||
"SPS Exam for SEB Server Exam: {} don't exists yet, create necessary structures on SPS",
|
||||
exam.externalId);
|
||||
|
||||
exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate, spsData));
|
||||
exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate));
|
||||
createSEBAccess(exam, apiTemplate, spsData);
|
||||
createExam(exam, apiTemplate, spsData);
|
||||
exam.supporter.forEach(userUUID -> createExamReadPrivilege(userUUID, spsData.spsExamUUID, apiTemplate));
|
||||
|
@ -348,12 +351,37 @@ class ScreenProctoringAPIBinding {
|
|||
});
|
||||
}
|
||||
|
||||
public void synchronizeUserAccounts(final Exam exam) {
|
||||
void synchronizeUserAccount(final String userUUID) {
|
||||
try {
|
||||
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(null);
|
||||
// check if user exists on SPS
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.USER_ACCOUNT_ENDPOINT + userUUID)
|
||||
.build()
|
||||
.toUriString();
|
||||
|
||||
final ResponseEntity<String> exchange = apiTemplate.exchange(
|
||||
uri, HttpMethod.POST, null, apiTemplate.getHeaders());
|
||||
|
||||
if (exchange.getStatusCode() == HttpStatus.OK) {
|
||||
log.info("Synchronize SPS user account for SEB Server user account with id: {} ", userUUID);
|
||||
this.synchronizeUserAccount(userUUID, apiTemplate);
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to synchronize user account with SPS for user: {}", userUUID);
|
||||
}
|
||||
}
|
||||
|
||||
void synchronizeUserAccounts(final Exam exam) {
|
||||
try {
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
|
||||
exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate, spsData));
|
||||
exam.supporter.forEach(userUUID -> synchronizeUserAccount(userUUID, apiTemplate));
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to synchronize user accounts with SPS for exam: {}", exam);
|
||||
}
|
||||
|
@ -363,14 +391,14 @@ class ScreenProctoringAPIBinding {
|
|||
*
|
||||
* @param exam The exam
|
||||
* @return Result refer to the exam or to an error when happened */
|
||||
public Result<Exam> updateExam(final Exam exam) {
|
||||
Result<Exam> updateExam(final Exam exam) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.EXAM_ENDPOINT)
|
||||
.pathSegment(spsData.spsExamUUID)
|
||||
.build()
|
||||
|
@ -381,8 +409,8 @@ class ScreenProctoringAPIBinding {
|
|||
exam.getDescription(),
|
||||
exam.getStartURL(),
|
||||
exam.getType().name(),
|
||||
exam.startTime.getMillis(),
|
||||
exam.endTime.getMillis());
|
||||
exam.startTime != null ? exam.startTime.getMillis() : null,
|
||||
exam.endTime != null ? exam.endTime.getMillis() : null);
|
||||
|
||||
final String jsonExamUpdate = this.jsonMapper.writeValueAsString(examUpdate);
|
||||
|
||||
|
@ -404,7 +432,7 @@ class ScreenProctoringAPIBinding {
|
|||
*
|
||||
* @param exam The exam
|
||||
* @return Result refer to the exam or to an error when happened */
|
||||
public Result<Exam> dispsoseScreenProctoring(final Exam exam) {
|
||||
Result<Exam> disposeScreenProctoring(final Exam exam) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
|
@ -414,7 +442,7 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, false, apiTemplate);
|
||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, false, apiTemplate);
|
||||
|
||||
// mark successfully dispose on SPS side
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
|
@ -432,7 +460,7 @@ class ScreenProctoringAPIBinding {
|
|||
*
|
||||
* @param exam The exam
|
||||
* @return Result refer to the exam or to an error when happened */
|
||||
public Result<Exam> deleteScreenProctoring(final Exam exam) {
|
||||
Result<Exam> deleteScreenProctoring(final Exam exam) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
|
@ -446,7 +474,7 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, apiTemplate);
|
||||
deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, apiTemplate);
|
||||
|
||||
// mark successfully dispose on SPS side
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
|
@ -459,7 +487,7 @@ class ScreenProctoringAPIBinding {
|
|||
});
|
||||
}
|
||||
|
||||
public Result<ScreenProctoringGroup> createGroup(
|
||||
Result<ScreenProctoringGroup> createGroup(
|
||||
final String spsExamUUID,
|
||||
final int groupNumber,
|
||||
final String description,
|
||||
|
@ -467,14 +495,17 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
return Result.tryCatch(() -> {
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
final ScreenProctoringSettings settings = this.proctoringSettingsDAO
|
||||
.getScreenProctoringSettings(new EntityKey(exam.id, EntityType.EXAM))
|
||||
.getOrThrow();
|
||||
|
||||
if (apiTemplate.screenProctoringSettings.collectingStrategy != CollectingStrategy.FIX_SIZE) {
|
||||
if (settings.collectingStrategy != CollectingStrategy.FIX_SIZE) {
|
||||
throw new IllegalStateException(
|
||||
"Only FIX_SIZE collecting strategy is supposed to create additional rooms");
|
||||
}
|
||||
|
||||
return createGroupOnSPS(
|
||||
apiTemplate.screenProctoringSettings.collectingGroupSize,
|
||||
settings.collectingGroupSize,
|
||||
exam.id,
|
||||
"Proctoring Group " + groupNumber + " : " + exam.getName(),
|
||||
description,
|
||||
|
@ -483,7 +514,7 @@ class ScreenProctoringAPIBinding {
|
|||
});
|
||||
}
|
||||
|
||||
public String createSEBSession(
|
||||
String createSEBSession(
|
||||
final Long examId,
|
||||
final ScreenProctoringGroup localGroup,
|
||||
final ClientConnectionRecord clientConnection) {
|
||||
|
@ -493,7 +524,7 @@ class ScreenProctoringAPIBinding {
|
|||
final String token = clientConnection.getConnectionToken();
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(examId);
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.SESSION_ENDPOINT)
|
||||
|
||||
.build()
|
||||
|
@ -518,19 +549,19 @@ class ScreenProctoringAPIBinding {
|
|||
return token;
|
||||
}
|
||||
|
||||
public void activateSEBAccessOnSPS(final Exam exam, final boolean activate) {
|
||||
void activateSEBAccessOnSPS(final Exam exam, final boolean activate) {
|
||||
try {
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
|
||||
activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, 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);
|
||||
}
|
||||
}
|
||||
|
||||
public void createExamReadPrivileges(final Exam exam) {
|
||||
void createExamReadPrivileges(final Exam exam) {
|
||||
try {
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
|
||||
final SPSData spsData = this.getSPSData(exam.id);
|
||||
|
@ -544,8 +575,7 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
private void synchronizeUserAccount(
|
||||
final String userUUID,
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate,
|
||||
final SPSData spsData) {
|
||||
final ScreenProctoringServiceOAuthTemplate apiTemplate) {
|
||||
|
||||
try {
|
||||
|
||||
|
@ -556,32 +586,17 @@ class ScreenProctoringAPIBinding {
|
|||
.sebServerUserByUsername(userInfo.name)
|
||||
.getOrThrow();
|
||||
|
||||
final UserMod userMod = new UserMod(
|
||||
userInfo.uuid,
|
||||
-1L,
|
||||
userInfo.name,
|
||||
userInfo.surname,
|
||||
userInfo.username,
|
||||
accountInfo.getPassword(),
|
||||
accountInfo.getPassword(),
|
||||
userInfo.email,
|
||||
userInfo.language,
|
||||
userInfo.timeZone,
|
||||
userInfo.roles);
|
||||
|
||||
final UserMod userMod = getUserModifications(userInfo, accountInfo);
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.USERSYNC_SEBSERVER_ENDPOINT)
|
||||
.build()
|
||||
.toUriString();
|
||||
|
||||
final String jsonBody = this.jsonMapper.writeValueAsString(userMod);
|
||||
|
||||
final ResponseEntity<String> exchange = apiTemplate.exchange(
|
||||
uri,
|
||||
HttpMethod.POST,
|
||||
jsonBody,
|
||||
apiTemplate.getHeadersJSONRequest());
|
||||
uri, HttpMethod.POST, jsonBody, apiTemplate.getHeadersJSONRequest());
|
||||
|
||||
if (exchange.getStatusCode() != HttpStatus.OK) {
|
||||
log.warn("Failed to synchronize user account on SPS: {}", exchange);
|
||||
} else {
|
||||
|
@ -594,6 +609,28 @@ class ScreenProctoringAPIBinding {
|
|||
}
|
||||
}
|
||||
|
||||
private static UserMod getUserModifications(final UserInfo userInfo, final SEBServerUser accountInfo) {
|
||||
final Set<String> spsUserRoles = new HashSet<>();
|
||||
spsUserRoles.add(SPS_API.SPSUserRole.PROCTOR.name());
|
||||
if (userInfo.roles.contains(UserRole.SEB_SERVER_ADMIN.name()) ||
|
||||
userInfo.roles.contains(UserRole.INSTITUTIONAL_ADMIN.name())) {
|
||||
spsUserRoles.add(SPS_API.SPSUserRole.ADMIN.name());
|
||||
}
|
||||
|
||||
return new UserMod(
|
||||
userInfo.uuid,
|
||||
-1L,
|
||||
userInfo.name,
|
||||
userInfo.surname,
|
||||
userInfo.username,
|
||||
accountInfo.getPassword(),
|
||||
accountInfo.getPassword(),
|
||||
userInfo.email,
|
||||
userInfo.language,
|
||||
userInfo.timeZone,
|
||||
spsUserRoles);
|
||||
}
|
||||
|
||||
private void createExamReadPrivilege(
|
||||
final String userUUID,
|
||||
final String examUUID,
|
||||
|
@ -606,8 +643,8 @@ class ScreenProctoringAPIBinding {
|
|||
.getOrThrow();
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.path(SPS_API.ENTIY_PRIVILEGES_ENDPOINT)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.ENTITY_PRIVILEGES_ENDPOINT)
|
||||
.build()
|
||||
.toUriString();
|
||||
|
||||
|
@ -646,13 +683,16 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
try {
|
||||
|
||||
final ScreenProctoringSettings settings = this.proctoringSettingsDAO
|
||||
.getScreenProctoringSettings(new EntityKey(exam.id, EntityType.EXAM))
|
||||
.getOrThrow();
|
||||
final List<ScreenProctoringGroup> result = new ArrayList<>();
|
||||
|
||||
switch (apiTemplate.screenProctoringSettings.collectingStrategy) {
|
||||
switch (settings.collectingStrategy) {
|
||||
|
||||
case FIX_SIZE: {
|
||||
result.add(createGroupOnSPS(
|
||||
apiTemplate.screenProctoringSettings.collectingGroupSize,
|
||||
settings.collectingGroupSize,
|
||||
exam.id,
|
||||
"Group 1 : " + exam.getName(),
|
||||
"Created by SEB Server",
|
||||
|
@ -699,7 +739,7 @@ class ScreenProctoringAPIBinding {
|
|||
throws JsonMappingException, JsonProcessingException {
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.GROUP_ENDPOINT)
|
||||
.build()
|
||||
.toUriString();
|
||||
|
@ -732,7 +772,7 @@ class ScreenProctoringAPIBinding {
|
|||
try {
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.EXAM_ENDPOINT)
|
||||
.build().toUriString();
|
||||
|
||||
|
@ -776,7 +816,7 @@ class ScreenProctoringAPIBinding {
|
|||
final String description = "This SEB access was auto-generated by SEB Server";
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.SEB_ACCESS_ENDPOINT)
|
||||
.build()
|
||||
.toUriString();
|
||||
|
@ -794,7 +834,7 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
// store SEB access data for proctoring along with the exam
|
||||
final JsonNode requestJSON = this.jsonMapper.readTree(exchange.getBody());
|
||||
spsData.spsSEBAccesUUID = requestJSON.get(SPS_API.SEB_ACCESS.ATTR_UUID).textValue();
|
||||
spsData.spsSEBAccessUUID = requestJSON.get(SPS_API.SEB_ACCESS.ATTR_UUID).textValue();
|
||||
spsData.spsSEBAccessName = requestJSON.get(SPS_API.SEB_ACCESS.ATTR_CLIENT_NAME).textValue();
|
||||
spsData.spsSEBAccessPWD = requestJSON.get(SPS_API.SEB_ACCESS.ATTR_CLIENT_SECRET).textValue();
|
||||
|
||||
|
@ -819,7 +859,7 @@ class ScreenProctoringAPIBinding {
|
|||
try {
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(domainPath)
|
||||
.pathSegment(uuid)
|
||||
.pathSegment(activate ? SPS_API.ACTIVE_PATH_SEGMENT : SPS_API.INACTIVE_PATH_SEGMENT)
|
||||
|
@ -843,7 +883,7 @@ class ScreenProctoringAPIBinding {
|
|||
try {
|
||||
|
||||
final String uri = UriComponentsBuilder
|
||||
.fromUriString(apiTemplate.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(domainPath)
|
||||
.pathSegment(uuid)
|
||||
.build()
|
||||
|
@ -851,10 +891,10 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.DELETE);
|
||||
if (exchange.getStatusCode() != HttpStatus.OK) {
|
||||
log.error("Failed to delete on SPS: {} with response: ", uri, exchange);
|
||||
log.error("Failed to delete on SPS: {} with response: {}", uri, exchange);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to delete on SPS: {}, {}, {}", domainPath, uuid, e);
|
||||
log.error("Failed to delete on SPS: {}, {}, ", domainPath, uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -878,13 +918,13 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(spsData.spsSEBAccesUUID)) {
|
||||
if (StringUtils.isNotBlank(spsData.spsSEBAccessUUID)) {
|
||||
log.info(
|
||||
"Try to rollback SPS SEB Access with UUID: {} for exam: {}",
|
||||
spsData.spsSEBAccesUUID,
|
||||
spsData.spsSEBAccessUUID,
|
||||
exam.externalId);
|
||||
|
||||
deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccesUUID, apiTemplate);
|
||||
deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, apiTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -892,16 +932,34 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
private ScreenProctoringServiceOAuthTemplate getAPITemplate(final Long examId) {
|
||||
if (this.apiTemplate == null || !this.apiTemplate.isValid(examId)) {
|
||||
if (examId != null) {
|
||||
|
||||
log.debug("Create new ScreenProctoringServiceOAuthTemplate for exam: {}", examId);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Create new ScreenProctoringServiceOAuthTemplate for exam: {}", examId);
|
||||
}
|
||||
|
||||
final ScreenProctoringSettings settings = this.proctoringSettingsDAO
|
||||
.getScreenProctoringSettings(new EntityKey(examId, EntityType.EXAM))
|
||||
.getOrThrow();
|
||||
final ScreenProctoringSettings settings = this.proctoringSettingsDAO
|
||||
.getScreenProctoringSettings(new EntityKey(examId, EntityType.EXAM))
|
||||
.getOrThrow();
|
||||
this.testConnection(settings).getOrThrow();
|
||||
this.apiTemplate = new ScreenProctoringServiceOAuthTemplate(this, settings);
|
||||
|
||||
this.testConnection(settings).getOrThrow();
|
||||
} else if (this.webserviceInfo.getScreenProctoringServiceBundle().bundled) {
|
||||
|
||||
this.apiTemplate = new ScreenProctoringServiceOAuthTemplate(this, settings);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Create new ScreenProctoringServiceOAuthTemplate for exam: {}", examId);
|
||||
}
|
||||
|
||||
WebserviceInfo.ScreenProctoringServiceBundle bundle = this.webserviceInfo
|
||||
.getScreenProctoringServiceBundle();
|
||||
|
||||
this.testConnection(bundle).getOrThrow();
|
||||
this.apiTemplate = new ScreenProctoringServiceOAuthTemplate(this, bundle);
|
||||
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("No SPS API access information found!");
|
||||
}
|
||||
}
|
||||
|
||||
return this.apiTemplate;
|
||||
|
@ -913,9 +971,8 @@ class ScreenProctoringAPIBinding {
|
|||
private static final List<String> SCOPES = Collections.unmodifiableList(
|
||||
Arrays.asList("read", "write"));
|
||||
|
||||
private final ScreenProctoringSettings screenProctoringSettings;
|
||||
private final SPSAPIAccessData spsAPIAccessData;
|
||||
private final CircuitBreaker<ResponseEntity<String>> circuitBreaker;
|
||||
|
||||
private final ResourceOwnerPasswordResourceDetails resource;
|
||||
private final ClientCredentials clientCredentials;
|
||||
private final ClientCredentials userCredentials;
|
||||
|
@ -923,32 +980,31 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
ScreenProctoringServiceOAuthTemplate(
|
||||
final ScreenProctoringAPIBinding sebScreenProctoringService,
|
||||
final ScreenProctoringSettings screenProctoringSettings) {
|
||||
final SPSAPIAccessData spsAPIAccessData) {
|
||||
|
||||
this.screenProctoringSettings = screenProctoringSettings;
|
||||
this.spsAPIAccessData = spsAPIAccessData;
|
||||
this.circuitBreaker = sebScreenProctoringService.asyncService.createCircuitBreaker(
|
||||
2,
|
||||
10 * Constants.SECOND_IN_MILLIS,
|
||||
10 * Constants.SECOND_IN_MILLIS);
|
||||
|
||||
this.clientCredentials = new ClientCredentials(
|
||||
this.screenProctoringSettings.spsAPIKey,
|
||||
this.screenProctoringSettings.spsAPISecret);
|
||||
spsAPIAccessData.getSpsAPIKey(),
|
||||
spsAPIAccessData.getSpsAPISecret());
|
||||
|
||||
CharSequence decryptedSecret = sebScreenProctoringService.cryptor
|
||||
.decrypt(this.clientCredentials.secret)
|
||||
.getOrThrow();
|
||||
|
||||
this.resource = new ResourceOwnerPasswordResourceDetails();
|
||||
this.resource.setAccessTokenUri(this.screenProctoringSettings.spsServiceURL + SPS_API.TOKEN_ENDPOINT);
|
||||
this.resource.setAccessTokenUri(spsAPIAccessData.getSpsServiceURL() + SPS_API.TOKEN_ENDPOINT);
|
||||
this.resource.setClientId(this.clientCredentials.clientIdAsString());
|
||||
this.resource.setClientSecret(decryptedSecret.toString());
|
||||
this.resource.setGrantType(GRANT_TYPE);
|
||||
this.resource.setScope(SCOPES);
|
||||
|
||||
this.userCredentials = new ClientCredentials(
|
||||
this.screenProctoringSettings.spsAccountId,
|
||||
this.screenProctoringSettings.spsAccountPassword);
|
||||
spsAPIAccessData.getSpsAccountId(),
|
||||
spsAPIAccessData.getSpsAccountPassword());
|
||||
|
||||
decryptedSecret = sebScreenProctoringService.cryptor
|
||||
.decrypt(this.userCredentials.secret)
|
||||
|
@ -976,7 +1032,7 @@ class ScreenProctoringAPIBinding {
|
|||
try {
|
||||
|
||||
final String url = UriComponentsBuilder
|
||||
.fromUriString(this.screenProctoringSettings.spsServiceURL)
|
||||
.fromUriString(this.spsAPIAccessData.getSpsServiceURL())
|
||||
.path(SPS_API.TEST_ENDPOINT)
|
||||
.queryParam("pageSize", "1")
|
||||
.queryParam("pageNumber", "1")
|
||||
|
@ -994,7 +1050,7 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
boolean isValid(final Long examId) {
|
||||
|
||||
if (this.screenProctoringSettings.examId != examId) {
|
||||
if (!Objects.equals(this.spsAPIAccessData.getExamId(), examId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1010,12 +1066,8 @@ class ScreenProctoringAPIBinding {
|
|||
return false;
|
||||
}
|
||||
|
||||
final int expiresIn = accessToken.getExpiresIn();
|
||||
if (expiresIn < 60) {
|
||||
return false;
|
||||
}
|
||||
return accessToken.getExpiresIn() >= 60;
|
||||
|
||||
return true;
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to verify SEB Screen Proctoring OAuth2RestTemplate status", e);
|
||||
return false;
|
||||
|
@ -1092,14 +1144,10 @@ class ScreenProctoringAPIBinding {
|
|||
public static final String ATTR_SPS_ACTIVE = "spsExamActive";
|
||||
public static final String ATTR_SPS_ACCESS_DATA = "spsAccessData";
|
||||
|
||||
@JsonProperty("spsUserUUID")
|
||||
String spsUserUUID = null;
|
||||
@JsonProperty("spsUserName")
|
||||
String spsUserName = null;
|
||||
@JsonProperty("spsUserPWD")
|
||||
String spsUserPWD = null;
|
||||
@JsonProperty("spsSEBAccesUUID")
|
||||
String spsSEBAccesUUID = null;
|
||||
@JsonProperty("spsSEBAccessUUID")
|
||||
String spsSEBAccessUUID = null;
|
||||
@JsonProperty("spsSEBAccessName")
|
||||
String spsSEBAccessName = null;
|
||||
@JsonProperty("spsSEBAccessPWD")
|
||||
|
@ -1112,18 +1160,16 @@ class ScreenProctoringAPIBinding {
|
|||
|
||||
@JsonCreator
|
||||
public SPSData(
|
||||
@JsonProperty("spsUserUUID") final String spsUserUUID,
|
||||
@JsonProperty("spsUserName") final String spsUserName,
|
||||
@JsonProperty("spsUserPWD") final String spsUserPWD,
|
||||
@JsonProperty("spsSEBAccessUUID") final String spsSEBAccessUUID,
|
||||
// NOTE: this is only for compatibility reasons, TODO as soon as possible
|
||||
@JsonProperty("spsSEBAccesUUID") final String spsSEBAccesUUID,
|
||||
@JsonProperty("spsSEBAccessName") final String spsSEBAccessName,
|
||||
@JsonProperty("spsSEBAccessPWD") final String spsSEBAccessPWD,
|
||||
@JsonProperty("psExamUUID") final String spsExamUUID) {
|
||||
|
||||
this.spsUserUUID = spsUserUUID;
|
||||
this.spsUserName = spsUserName;
|
||||
this.spsUserPWD = spsUserPWD;
|
||||
this.spsSEBAccesUUID = spsSEBAccesUUID;
|
||||
this.spsSEBAccessUUID = StringUtils.isNotBlank(spsSEBAccesUUID) ? spsSEBAccesUUID : spsSEBAccessUUID;
|
||||
this.spsSEBAccessName = spsSEBAccessName;
|
||||
this.spsSEBAccessPWD = spsSEBAccessPWD;
|
||||
this.spsExamUUID = spsExamUUID;
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Collection;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -59,7 +60,6 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
private static final Logger log = LoggerFactory.getLogger(ScreenProctoringServiceImpl.class);
|
||||
|
||||
private final Cryptor cryptor;
|
||||
private final JSONMapper jsonMapper;
|
||||
private final ScreenProctoringAPIBinding screenProctoringAPIBinding;
|
||||
private final ScreenProctoringGroupDAO screenProctoringGroupDAO;
|
||||
private final ProctoringSettingsDAO proctoringSettingsDAO;
|
||||
|
@ -67,6 +67,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
private final ExamDAO examDAO;
|
||||
private final SEBClientInstructionService sebInstructionService;
|
||||
private final ExamSessionCacheService examSessionCacheService;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
|
||||
public ScreenProctoringServiceImpl(
|
||||
final Cryptor cryptor,
|
||||
|
@ -79,24 +80,25 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
final AdditionalAttributesDAO additionalAttributesDAO,
|
||||
final ScreenProctoringGroupDAO screenProctoringGroupDAO,
|
||||
final SEBClientInstructionService sebInstructionService,
|
||||
final ExamSessionCacheService examSessionCacheService) {
|
||||
final ExamSessionCacheService examSessionCacheService,
|
||||
final WebserviceInfo webserviceInfo) {
|
||||
|
||||
this.cryptor = cryptor;
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.examDAO = examDAO;
|
||||
this.screenProctoringGroupDAO = screenProctoringGroupDAO;
|
||||
this.clientConnectionDAO = clientConnectionDAO;
|
||||
this.sebInstructionService = sebInstructionService;
|
||||
this.examSessionCacheService = examSessionCacheService;
|
||||
this.proctoringSettingsDAO = proctoringSettingsDAO;
|
||||
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.screenProctoringAPIBinding = new ScreenProctoringAPIBinding(
|
||||
userDAO,
|
||||
cryptor,
|
||||
asyncService,
|
||||
jsonMapper,
|
||||
proctoringSettingsDAO,
|
||||
additionalAttributesDAO);
|
||||
additionalAttributesDAO,
|
||||
webserviceInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -161,7 +163,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> applyScreenProctoingForExam(final Long examId) {
|
||||
public Result<Exam> applyScreenProctoringForExam(final Long examId) {
|
||||
|
||||
return this.examDAO
|
||||
.byPK(examId)
|
||||
|
@ -187,7 +189,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
} else if (!isEnabling && isSPSActive) {
|
||||
|
||||
this.screenProctoringAPIBinding
|
||||
.dispsoseScreenProctoring(exam)
|
||||
.disposeScreenProctoring(exam)
|
||||
.onError(error -> log.error("Failed to dispose screen proctoring for exam: {}",
|
||||
exam,
|
||||
error))
|
||||
|
@ -205,7 +207,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> updateExamOnScreenProctoingService(final Long examId) {
|
||||
public Result<Exam> updateExamOnScreenProctoringService(final Long examId) {
|
||||
return this.examDAO.byPK(examId)
|
||||
.map(exam -> {
|
||||
|
||||
|
@ -213,10 +215,10 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
log.debug("Update changed exam attributes for screen proctoring: {}", exam);
|
||||
}
|
||||
|
||||
final String enabeld = exam.additionalAttributes
|
||||
final String enabled = exam.additionalAttributes
|
||||
.get(ScreenProctoringSettings.ATTR_ENABLE_SCREEN_PROCTORING);
|
||||
|
||||
if (!BooleanUtils.toBoolean(enabeld)) {
|
||||
if (!BooleanUtils.toBoolean(enabled)) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
|
@ -239,7 +241,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
.flatMap(this.clientConnectionDAO::getAllForScreenProctoringUpdate)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.forEach(cc -> applyScreenProctoringSession(cc));
|
||||
.forEach(this::applyScreenProctoringSession);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to update active SEB connections for screen proctoring");
|
||||
|
@ -248,10 +250,10 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
|
||||
@Override
|
||||
public void notifyExamSaved(final Exam exam) {
|
||||
final String enabeld = exam.additionalAttributes
|
||||
final String enabled = exam.additionalAttributes
|
||||
.get(ScreenProctoringSettings.ATTR_ENABLE_SCREEN_PROCTORING);
|
||||
|
||||
if (!BooleanUtils.toBoolean(enabeld)) {
|
||||
if (!BooleanUtils.toBoolean(enabled)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -259,6 +261,16 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
this.screenProctoringAPIBinding.createExamReadPrivileges(exam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeSPSUser(final String userUUID) {
|
||||
|
||||
if (!webserviceInfo.getScreenProctoringServiceBundle().bundled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.screenProctoringAPIBinding.synchronizeUserAccount(userUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyExamStarted(final ExamStartedEvent event) {
|
||||
final Exam exam = event.exam;
|
||||
|
@ -321,7 +333,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
.getOrThrow();
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to apply screen proctoring session to SEB with connection: ", ccRecord, e);
|
||||
log.error("Failed to apply screen proctoring session to SEB with connection: {}", ccRecord, e);
|
||||
|
||||
if (placeReservedInGroup != null) {
|
||||
// release reserved place in group
|
||||
|
@ -361,13 +373,13 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
}
|
||||
|
||||
private ScreenProctoringGroup applyToDefaultGroup(
|
||||
final Long connectioId,
|
||||
final Long connectionId,
|
||||
final String connectionToken,
|
||||
final Exam exam) {
|
||||
|
||||
final ScreenProctoringGroup screenProctoringGroup = reservePlaceOnProctoringGroup(exam);
|
||||
this.clientConnectionDAO.assignToScreenProctoringGroup(
|
||||
connectioId,
|
||||
connectionId,
|
||||
connectionToken,
|
||||
screenProctoringGroup.id)
|
||||
.getOrThrow();
|
||||
|
@ -405,7 +417,9 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
|
||||
private ScreenProctoringGroup applyNewGroup(final Exam exam, final Integer groupSize) {
|
||||
|
||||
final String spsExamUUID = this.getSPSData(exam).spsExamUUID;
|
||||
final String spsExamUUID = this.screenProctoringAPIBinding
|
||||
.getSPSData(exam.id)
|
||||
.spsExamUUID;
|
||||
|
||||
return this.screenProctoringGroupDAO
|
||||
.getCollectingGroups(exam.id)
|
||||
|
@ -458,7 +472,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
log.debug("Register JOIN instruction for client ");
|
||||
}
|
||||
|
||||
final SPSData spsData = getSPSData(exam);
|
||||
final SPSData spsData = this.screenProctoringAPIBinding.getSPSData(exam.id);
|
||||
final String url = exam.additionalAttributes.get(ScreenProctoringSettings.ATTR_SPS_SERVICE_URL);
|
||||
final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
|
@ -483,21 +497,4 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
ccRecord,
|
||||
error));
|
||||
}
|
||||
|
||||
// TODO make this with caching if performance is not good
|
||||
private SPSData getSPSData(final Exam exam) {
|
||||
try {
|
||||
|
||||
final String dataEncrypted = exam.additionalAttributes.get(SPSData.ATTR_SPS_ACCESS_DATA);
|
||||
|
||||
return this.jsonMapper.readValue(
|
||||
this.cryptor.decrypt(dataEncrypted).getOrThrow().toString(),
|
||||
SPSData.class);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to get local SPSData for exam: {}", exam);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.List;
|
|||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
@ -61,6 +62,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
|||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
private final UserDAO userDAO;
|
||||
private final PasswordEncoder userPasswordEncoder;
|
||||
private final ScreenProctoringService screenProctoringService;
|
||||
|
||||
public UserAccountController(
|
||||
final UserDAO userDAO,
|
||||
|
@ -70,6 +72,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
|||
final BulkActionService bulkActionService,
|
||||
final ApplicationEventPublisher applicationEventPublisher,
|
||||
final BeanValidationService beanValidationService,
|
||||
final ScreenProctoringService screenProctoringService,
|
||||
@Qualifier(WebSecurityConfig.USER_PASSWORD_ENCODER_BEAN_NAME) final PasswordEncoder userPasswordEncoder) {
|
||||
|
||||
super(authorization,
|
||||
|
@ -81,6 +84,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
|||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
this.userDAO = userDAO;
|
||||
this.userPasswordEncoder = userPasswordEncoder;
|
||||
this.screenProctoringService = screenProctoringService;
|
||||
}
|
||||
|
||||
@RequestMapping(path = API.CURRENT_USER_PATH_SEGMENT, method = RequestMethod.GET)
|
||||
|
@ -145,6 +149,13 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
|||
.flatMap(this::additionalConsistencyChecks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<UserInfo> notifySaved(final UserInfo entity) {
|
||||
final Result<UserInfo> userInfoResult = super.notifySaved(entity);
|
||||
this.synchronizeUserWithSPS(entity);
|
||||
return userInfoResult;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.PASSWORD_PATH_SEGMENT,
|
||||
method = RequestMethod.PUT,
|
||||
|
@ -159,6 +170,7 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
|||
.flatMap(e -> this.userDAO.changePassword(modelId, passwordChange.getNewPassword()))
|
||||
.flatMap(this::revokeAccessToken)
|
||||
.flatMap(e -> this.userActivityLogDAO.log(UserLogActivityType.PASSWORD_CHANGE, e))
|
||||
.map(this::synchronizeUserWithSPS)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
@ -258,6 +270,10 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
|
|||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private UserInfo synchronizeUserWithSPS(final UserInfo userInfo) {
|
||||
screenProctoringService.synchronizeSPSUser(userInfo.uuid);
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue