SEBSERV-417 and SEBSP-111

This commit is contained in:
anhefti 2024-06-13 13:11:08 +02:00
parent c161e3c5ef
commit 8155d155c0
7 changed files with 58 additions and 89 deletions

View file

@ -178,9 +178,9 @@ public final class API {
public static final String LMS_FULL_INTEGRATION_EXAM_TEMPLATE_ID = "exam_template_id";
public static final String LMS_FULL_INTEGRATION_QUIT_PASSWORD = "quit_password";
public static final String LMS_FULL_INTEGRATION_QUIT_LINK = "quit_link";
public static final String LMS_FULL_INTEGRATION_USER_ID = "userid_id";
public static final String LMS_FULL_INTEGRATION_USER_NAME = "userid_username ";
public static final String LMS_FULL_INTEGRATION_USER_EMAIL = "userid_email";
public static final String LMS_FULL_INTEGRATION_USER_ID = "user_id";
public static final String LMS_FULL_INTEGRATION_USER_NAME = "user_username";
public static final String LMS_FULL_INTEGRATION_USER_EMAIL = "user_email";
public static final String LMS_FULL_INTEGRATION_USER_FIRST_NAME = "user_firstname";
public static final String LMS_FULL_INTEGRATION_USER_LAST_NAME = "user_lastname";

View file

@ -288,6 +288,8 @@ public class OAuth2AuthorizationContextHolder implements AuthorizationContextHol
}
final TokenLoginInfo loginInfo = response.getBody();
this.resource.setUsername(loginInfo.username);
this.resource.setPassword(loginInfo.userUUID);
this.restTemplate.getOAuth2ClientContext().setAccessToken(loginInfo.login);
loginForward = loginInfo.login_forward;

View file

@ -27,11 +27,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

View file

@ -18,8 +18,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationServi
public interface TeacherAccountService {
/** Creates an Ad-Hoc Teacher account for a given existing Exam.
* This also checks if such an account already exists and if so,
* it uses that and activates it if not already active
*
* @param exam The Exam instance
* @param adHocAccountData The account data for new Ad-Hoc account
@ -38,7 +36,11 @@ public interface TeacherAccountService {
default String getTeacherAccountIdentifier(
final Exam exam,
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) {
return getTeacherAccountIdentifier(exam.getModelId(), adHocAccountData.userId);
return getTeacherAccountIdentifier(
exam.getModelId(),
String.valueOf(exam.lmsSetupId),
adHocAccountData.userId);
}
/** Get the identifier for certain Teacher account for specified Exam.
@ -47,7 +49,7 @@ public interface TeacherAccountService {
* @param userId the account id
* @return account identifier
*/
String getTeacherAccountIdentifier(String examId, String userId);
String getTeacherAccountIdentifier(String examId, String lmsId, String userId);
/** Deactivates a certain ad-hoc Teacher account
* Usually called when an exam is deleted. Checks if Teacher account for exam

View file

@ -77,12 +77,16 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
}
@Override
public String getTeacherAccountIdentifier(final String examId, final String userId) {
if (examId == null || userId == null) {
public String getTeacherAccountIdentifier(
final String examId,
final String lmsId,
final String userId) {
if (examId == null || lmsId == null || userId == null) {
throw new RuntimeException("examId and/or userId cannot be null");
}
return userId;
return examId + Constants.UNDERLINE + lmsId + Constants.UNDERLINE + userId;
}
@Override
@ -148,7 +152,6 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
.byModelId(getTeacherAccountIdentifier(exam, adHocAccountData))
.onErrorDo(error -> handleAccountDoesNotExistYet(createIfNotExists, exam, adHocAccountData))
.map(account -> applySupporter(account, exam))
.map(account -> synchronizeSPSUserForExam(account, exam.id))
.map(account -> this.createOneTimeToken(account, exam.id));
}
@ -191,7 +194,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
key,
"MONITOR_EXAM_FROM_LIST");
return new TokenLoginInfo(user.username, user.uuid, loginForward, token);
return new TokenLoginInfo(user.username, claims.getSubject(), loginForward, token);
});
}
@ -231,6 +234,7 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
final String subjectClaim = UUID.randomUUID().toString();
userDAO.changePassword(account.uuid, subjectClaim);
synchronizeSPSUserForExam(account, examId);
final Map<String, Object> claims = new HashMap<>();
claims.put(USER_CLAIM, account.uuid);

View file

@ -31,14 +31,10 @@ public interface FullLmsIntegrationService {
@EventListener
void notifyLmsSetupChange(final LmsSetupChangeEvent event);
//Result<LmsSetup> applyLMSSetupDeactivation(LmsSetup lmsSetup);
@EventListener
void notifyExamTemplateChange(final ExamTemplateChangeEvent event);
@EventListener(ConnectionConfigurationChangeEvent.class)
void notifyConnectionConfigurationChange(ConnectionConfigurationChangeEvent event);
@EventListener(ExamDeletionEvent.class)
void notifyExamDeletion(ExamDeletionEvent event);

View file

@ -332,9 +332,9 @@ class ScreenProctoringAPIBinding {
final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.GET);
if (exchange.getStatusCode() != HttpStatus.NOT_FOUND) {
if (exchange.getStatusCode() == HttpStatus.NOT_FOUND) {
return false;
} else if (exchange.getStatusCode() != HttpStatus.OK) {
} else if (exchange.getStatusCode() == HttpStatus.OK) {
return true;
} else {
log.warn("Failed to verify if Exam on SPS already exists: {}", exchange.getBody());
@ -468,6 +468,10 @@ class ScreenProctoringAPIBinding {
return Result.tryCatch(() -> {
if (!this.isSPSActive(exam)) {
return exam;
}
if (log.isDebugEnabled()) {
log.debug("Deactivate active screen proctoring exam, groups and access on SPS for exam: {}", exam.name);
}
@ -629,56 +633,6 @@ class ScreenProctoringAPIBinding {
}
// /** This is called on exam delete and deletes the SEB Client Access and the ad-hoc User-Account
// * on Screen Proctoring Service side.
// * Also sends a exam delete request where Exam on SPS gets deleted if there are no session data for the exam
// *
// * @param exam The exam
// * @return Result refer to the exam or to an error when happened */
// Exam deleteScreenProctoring(final Exam exam) {
// try {
//
// if (!BooleanUtils.toBoolean(exam.additionalAttributes.get(SPSData.ATTR_SPS_ACTIVE))) {
// return exam;
// }
//
// if (log.isDebugEnabled()) {
// log.debug("Deactivate exam and groups on SPS site and send deletion request for exam {}", exam);
// }
//
// final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id);
// final SPSData spsData = this.getSPSData(exam.id);
// deletion(SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, apiTemplate);
// activation(exam, SPS_API.EXAM_ENDPOINT, spsData.spsExamUUID, false, apiTemplate);
//
// // exam delete request on SPS
// final String uri = UriComponentsBuilder
// .fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
// .path(SPS_API.EXAM_ENDPOINT)
// .pathSegment(spsData.spsExamUUID)
// .pathSegment(SPS_API.EXAM_DELETE_REQUEST_ENDPOINT)
// .build()
// .toUriString();
//
// final ResponseEntity<String> exchange = apiTemplate.exchange(uri, HttpMethod.DELETE);
// if (exchange.getStatusCode() != HttpStatus.OK) {
// log.error("Failed to request delete on SPS for Exam: {} with response: {}", exam, exchange);
// }
//
// // mark successfully dispose on SPS side
// this.additionalAttributesDAO.saveAdditionalAttribute(
// EntityType.EXAM,
// exam.id,
// SPSData.ATTR_SPS_ACTIVE,
// Constants.FALSE_STRING);
//
//
// } catch (final Exception e) {
// log.warn("Failed to apply SPS deletion of exam: {} error: {}", exam, e.getMessage());
// }
// return exam;
// }
Result<ScreenProctoringGroup> createGroup(
final String spsExamUUID,
final int groupNumber,
@ -741,23 +695,14 @@ class ScreenProctoringAPIBinding {
return token;
}
// void activateExamOnSPS(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.EXAM_ENDPOINT, spsData.spsSEBAccessUUID, activate, apiTemplate);
// activation(exam, SPS_API.SEB_ACCESS_ENDPOINT, spsData.spsSEBAccessUUID, activate, apiTemplate);
//
// } catch (final Exception e) {
// log.error("Failed to de/activate SEB Access on SPS for exam: {}", exam);
// }
// }
private void synchronizeUserAccount(
final String userUUID,
final ScreenProctoringServiceOAuthTemplate apiTemplate) {
if (UserService.LMS_INTEGRATION_CLIENT_UUID.equals(userUUID)) {
return;
}
try {
final UserInfo userInfo = this.userDAO
@ -919,7 +864,6 @@ class ScreenProctoringAPIBinding {
});
final String spsGroupUUID = groupAttributes.get(SPS_API.GROUP.ATTR_UUID);
return new ScreenProctoringGroup(null, examId, spsGroupUUID, name, size, exchange.getBody());
}
@ -1007,7 +951,33 @@ class ScreenProctoringAPIBinding {
final String name = SEB_SERVER_SCREEN_PROCTORING_SEB_ACCESS_PREFIX + exam.externalId;
final String description = "This SEB access was auto-generated by SEB Server";
final String uri = UriComponentsBuilder
// first try to get existing one by name and link it if available
String uri = UriComponentsBuilder
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
.path(SPS_API.SEB_ACCESS_ENDPOINT)
.queryParam(SPS_API.SEB_ACCESS.ATTR_NAME, name)
.build()
.toUriString();
final ResponseEntity<String> getResponse = apiTemplate.exchange(uri, HttpMethod.GET);
if (getResponse.getStatusCode() == HttpStatus.OK) {
try {
final JsonNode requestJSON = this.jsonMapper.readTree(getResponse.getBody());
final JsonNode content = requestJSON.get("content");
if (content.isArray()) {
final JsonNode sebConnection = content.get(0);
spsData.spsSEBAccessUUID = sebConnection.get(SPS_API.SEB_ACCESS.ATTR_UUID).textValue();
spsData.spsSEBAccessName = sebConnection.get(SPS_API.SEB_ACCESS.ATTR_CLIENT_NAME).textValue();
spsData.spsSEBAccessPWD = sebConnection.get(SPS_API.SEB_ACCESS.ATTR_CLIENT_SECRET).textValue();
return;
}
} catch (final Exception e) {
log.warn("Failed to extract existing SEB Account from JSON: {}", e.getMessage());
}
}
// otherwise create new one and link it
uri = UriComponentsBuilder
.fromUriString(apiTemplate.spsAPIAccessData.getSpsServiceURL())
.path(SPS_API.SEB_ACCESS_ENDPOINT)
.build()
@ -1022,7 +992,6 @@ class ScreenProctoringAPIBinding {
if (exchange.getStatusCode() != HttpStatus.OK) {
throw new RuntimeException("Failed to create SPS SEB access for exam: " + exam.externalId);
}
;
// store SEB access data for proctoring along with the exam
final JsonNode requestJSON = this.jsonMapper.readTree(exchange.getBody());