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