SEBSERV-435 improved SEB Server SPS user account sync

This commit is contained in:
anhefti 2023-11-29 14:21:28 +01:00
parent 6a0d53c8c4
commit 9bda8630f6
9 changed files with 81 additions and 14 deletions

View file

@ -180,7 +180,9 @@ public class MonitoringProctoringService {
proctoringGUIService,
room));
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.TOWN_HALL)) {
if (BooleanUtils.isTrue(proctoringSettings.enableProctoring) &&
proctoringSettings.enabledFeatures.contains(ProctoringFeature.TOWN_HALL)) {
updateTownhallButton(proctoringGUIService, pageContext);
}

View file

@ -499,8 +499,6 @@ public class UserDAOImpl implements UserDAO {
this.userRecordMapper
.selectByExample()
.where(UserRecordDynamicSqlSupport.username, isEqualTo(username))
.and(UserRecordDynamicSqlSupport.active,
isEqualTo(BooleanUtils.toInteger(true)))
.build()
.execute());
}

View file

@ -90,4 +90,7 @@ public interface ScreenProctoringService extends SessionUpdateTask {
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void synchronizeSPSUser(final String userUUID);
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
void deleteSPSUser(String userUUID);
}

View file

@ -494,8 +494,9 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
.getConnectionData(connectionToken)
.getOrThrow();
// A connection can only be disabled if we have a missing ping
if (!BooleanUtils.isTrue(connectionData.getMissingPing())) {
// A connection can only be disabled if we have a missing ping or for closed connections
if (connectionData.clientConnection.status != ConnectionStatus.CLOSED &&
!BooleanUtils.isTrue(connectionData.getMissingPing())) {
return connectionData.clientConnection;
}

View file

@ -363,7 +363,7 @@ class ScreenProctoringAPIBinding {
.toUriString();
final ResponseEntity<String> exchange = apiTemplate.exchange(
uri, HttpMethod.POST, null, apiTemplate.getHeaders());
uri, HttpMethod.GET, null, apiTemplate.getHeaders());
if (exchange.getStatusCode() == HttpStatus.OK) {
log.info("Synchronize SPS user account for SEB Server user account with id: {} ", userUUID);
@ -386,6 +386,30 @@ class ScreenProctoringAPIBinding {
log.error("Failed to synchronize user accounts with SPS for exam: {}", exam);
}
}
void deleteSPSUser(final String userUUID) {
try {
final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(null);
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.DELETE, null, apiTemplate.getHeaders());
if (exchange.getStatusCode() == HttpStatus.OK) {
log.info("Successfully deleted User Account on SPS for user: {}", userUUID);
} else {
log.error("Failed to delete user account on SPS for user: {} response: {}", userUUID, exchange);
}
} catch (final Exception e) {
log.error("Failed to delete user account on SPS for user: {}", userUUID);
}
}
/** This is called when an exam has changed its parameter and needs data update on SPS side
*
@ -583,7 +607,7 @@ class ScreenProctoringAPIBinding {
.byModelId(userUUID)
.getOrThrow();
final SEBServerUser accountInfo = this.userDAO
.sebServerUserByUsername(userInfo.name)
.sebServerUserByUsername(userInfo.username)
.getOrThrow();
final UserMod userMod = getUserModifications(userInfo, accountInfo);
@ -600,8 +624,24 @@ class ScreenProctoringAPIBinding {
if (exchange.getStatusCode() != HttpStatus.OK) {
log.warn("Failed to synchronize user account on SPS: {}", exchange);
} else {
log.info("Successfully synchronize user account on SPS for user: ");
log.info("Successfully synchronize user account on SPS for user: {}", userUUID);
}
// sync activity
final String activityURI = UriComponentsBuilder
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
.path(SPS_API.USER_ACCOUNT_ENDPOINT)
.pathSegment(userUUID)
.path(BooleanUtils.isTrue(userInfo.active) ? "/active" : "/inactive")
.build()
.toUriString();
final ResponseEntity<String> activityRequest = apiTemplate.exchange(
activityURI, HttpMethod.POST, jsonBody, apiTemplate.getHeaders());
if (activityRequest.getStatusCode() != HttpStatus.OK) {
log.warn("Failed to synchronize activity for user account on SPS: {}", activityRequest);
} else {
log.info("Successfully synchronize activity for user account on SPS for user: {}", userUUID);
}
} catch (final Exception e) {

View file

@ -15,12 +15,14 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
@ -262,6 +264,7 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
}
@Override
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
public void synchronizeSPSUser(final String userUUID) {
if (!webserviceInfo.getScreenProctoringServiceBundle().bundled) {
@ -271,6 +274,17 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
this.screenProctoringAPIBinding.synchronizeUserAccount(userUUID);
}
@Override
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
public void deleteSPSUser(final String userUUID) {
if (!webserviceInfo.getScreenProctoringServiceBundle().bundled) {
return;
}
this.screenProctoringAPIBinding.deleteSPSUser(userUUID);
}
@Override
public void notifyExamStarted(final ExamStartedEvent event) {
final Exam exam = event.exam;

View file

@ -15,6 +15,8 @@ import java.util.List;
import javax.validation.Valid;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.util.Pair;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
import org.mybatis.dynamic.sql.SqlTable;
import org.springframework.beans.factory.annotation.Qualifier;
@ -156,6 +158,13 @@ public class UserAccountController extends ActivatableEntityController<UserInfo,
return userInfoResult;
}
@Override
protected Result<Pair<UserInfo, EntityProcessingReport>> notifyDeleted(final Pair<UserInfo, EntityProcessingReport> pair) {
final Result<Pair<UserInfo, EntityProcessingReport>> result = super.notifyDeleted(pair);
this.screenProctoringService.deleteSPSUser(pair.a.uuid);
return result;
}
@RequestMapping(
path = API.PASSWORD_PATH_SEGMENT,
method = RequestMethod.PUT,

View file

@ -64,6 +64,6 @@ management.endpoints.web.exposure.include=logfile,loggers,jolokia
management.endpoints.web.path-mapping.jolokia=jmx
sebserver.feature.seb.screenProctoring.bundled=true
sebserver.feature.seb.screenProctoring.bundled.url=localhost:8090
sebserver.feature.seb.screenProctoring.bundled.url=http://localhost:8090
sebserver.feature.seb.screenProctoring.bundled.clientId=sebserverClient
sebserver.feature.seb.screenProctoring.bundled.sebserveraccount.username=SEBServerAPIAccount

View file

@ -2437,7 +2437,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
connections = connectionsCall.get();
assertFalse(connections.isEmpty());
conData = connections.iterator().next();
assertEquals("CLOSED", conData.clientConnection.status.name());
assertEquals("DISABLED", conData.clientConnection.status.name());
// get client logs
final Result<Page<ExtendedClientEvent>> clientLogPage = restService
@ -2520,7 +2520,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
assertFalse(ccDataPage.content.isEmpty());
final ClientConnectionData clientConnectionData = ccDataPage.content.get(0);
assertNotNull(clientConnectionData);
assertEquals("CLOSED", clientConnectionData.clientConnection.status.toString());
assertEquals("DISABLED", clientConnectionData.clientConnection.status.toString());
connectionDatacall = restService
.getBuilder(GetFinishedExamClientConnectionPage.class)
@ -2552,9 +2552,9 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
@Order(18)
// *************************************
// Use Case 18: Login as examAdmin2 and get dependencies of examAdmin2
// - Get all dependencies and check correctnes.
// - Get all dependencies including only Exam Configuration and check correctnes.
// - Get all dependencies including only ClientConnection and check correctnes.
// - Get all dependencies and check correctness.
// - Get all dependencies including only Exam Configuration and check correctness.
// - Get all dependencies including only ClientConnection and check correctness.
public void testUsecase18_UserDependencies() throws IOException {
final RestServiceImpl restService = createRestServiceForUser(
"examAdmin2",