From a6a9988cbed0e6915d75f8d07c2f394b8fbb06bf Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 15 Feb 2021 16:29:22 +0100 Subject: [PATCH] SEBSERV-139 added moderator flag to the jitsi meet room token --- .../session/ExamProctoringService.java | 16 ++++- .../impl/ExamJITSIProctoringService.java | 64 +++++++++++++------ .../impl/ExamJITSIProctoringServiceTest.java | 43 ++++++++++++- 3 files changed, 100 insertions(+), 23 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java index aa9fcb60..b7f3df2d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/ExamProctoringService.java @@ -16,10 +16,23 @@ import ch.ethz.seb.sebserver.gbl.util.Result; public interface ExamProctoringService { + /** Get the proctoring server type of the specific implementation + * + * @return the proctoring service type of the specific implementation */ ProctoringServerType getType(); + /** Use this to test the proctoring service settings against the remote proctoring server. + * + * @param examProctoring the settings to test + * @return Result refer to true if the settings are correct and the proctoring server can be accessed. */ Result testExamProctoring(final ProctoringSettings examProctoring); + /** Used to get the proctor's room connection data. + * + * @param proctoringSettings the proctoring settings + * @param roomName the name of the room + * @param subject name of the room + * @return SEBProctoringConnectionData that contains all connection data */ Result createProctorPublicRoomConnection( final ProctoringSettings proctoringSettings, final String roomName, @@ -59,7 +72,8 @@ public interface ExamProctoringService { final String clientKey, final String roomName, final String subject, - final Long expTime); + final Long expTime, + final boolean moderator); Result createClientAccessToken( final ProctoringSettings proctoringSettings, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java index e58eac80..e01da641 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringService.java @@ -46,7 +46,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService { "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; private static final String JITSI_ACCESS_TOKEN_PAYLOAD = - "{\"context\":{\"user\":{\"name\":\"%s\"}},\"iss\":\"%s\",\"aud\":\"%s\",\"sub\":\"%s\",\"room\":\"%s\"%s}"; + "{\"context\":{\"user\":{\"name\":\"%s\"}},\"iss\":\"%s\",\"aud\":\"%s\",\"sub\":\"%s\",\"room\":\"%s\"%s%s}"; private final RemoteProctoringRoomDAO remoteProctoringRoomDAO; private final AuthorizationService authorizationService; @@ -93,7 +93,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService { "seb-server", roomName, subject, - forExam(proctoringSettings)) + forExam(proctoringSettings), + true) .getOrThrow(); }); } @@ -131,7 +132,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService { "seb-client", roomName, connection.userSessionId, - forExam(proctoringSettings)) + forExam(proctoringSettings), + false) .getOrThrow(); }); } @@ -158,7 +160,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService { "seb-client", roomName, subject, - forExam(proctoringSettings)) + forExam(proctoringSettings), + false) .getOrThrow(); }); } @@ -209,7 +212,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService { "seb-client", roomName, subject, - expTime) + expTime, + false) .getOrThrow(); }); @@ -226,7 +230,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService { final String clientKey, final String roomName, final String subject, - final Long expTime) { + final Long expTime, + final boolean moderator) { return Result.tryCatch(() -> { @@ -242,7 +247,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService { clientKey, roomName, expTime, - host); + host, + moderator); return new SEBProctoringConnectionData( proctoringServerType, @@ -279,34 +285,27 @@ public class ExamJITSIProctoringService implements ExamProctoringService { "seb-client", roomName, forExam(proctoringSettings), - host); + host, + false); }); } - private String internalCreateAccessToken( + protected String internalCreateAccessToken( final String appKey, final CharSequence appSecret, final String clientName, final String clientKey, final String roomName, final Long expTime, - final String host) throws NoSuchAlgorithmException, InvalidKeyException { + final String host, + final boolean moderator) throws NoSuchAlgorithmException, InvalidKeyException { final StringBuilder builder = new StringBuilder(); final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding(); final String jwtHeaderPart = urlEncoder .encodeToString(JITSI_ACCESS_TOKEN_HEADER.getBytes(StandardCharsets.UTF_8)); - final String jwtPayload = String.format( - JITSI_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""), - clientName, - appKey, - clientKey, - host, - roomName, - (expTime != null) - ? String.format(",\"exp\":%s", String.valueOf(expTime)) - : ""); + final String jwtPayload = createPayload(appKey, clientName, clientKey, roomName, expTime, host, moderator); final String jwtPayloadPart = urlEncoder .encodeToString(jwtPayload.getBytes(StandardCharsets.UTF_8)); final String message = jwtHeaderPart + "." + jwtPayloadPart; @@ -324,6 +323,31 @@ public class ExamJITSIProctoringService implements ExamProctoringService { return builder.toString(); } + protected String createPayload( + final String appKey, + final String clientName, + final String clientKey, + final String roomName, + final Long expTime, + final String host, + final boolean moderator) { + + final String jwtPayload = String.format( + JITSI_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""), + clientName, + appKey, + clientKey, + host, + roomName, + (moderator) + ? ",\"moderator\":true" + : ",\"moderator\":false", + (expTime != null) + ? String.format(",\"exp\":%s", String.valueOf(expTime)) + : ""); + return jwtPayload; + } + private long forExam(final ProctoringSettings examProctoring) { if (examProctoring.examId == null) { throw new IllegalStateException("Missing exam identifier from ExamProctoring data"); diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java index 311d8b0d..bf5595ab 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/ExamJITSIProctoringServiceTest.java @@ -11,6 +11,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + import org.junit.Test; import org.mockito.Mockito; @@ -20,6 +23,40 @@ import ch.ethz.seb.sebserver.gbl.util.Cryptor; public class ExamJITSIProctoringServiceTest { + @Test + public void testTokenPayload() throws InvalidKeyException, NoSuchAlgorithmException { + final Cryptor cryptorMock = Mockito.mock(Cryptor.class); + Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123"); + final ExamJITSIProctoringService examJITSIProctoringService = + new ExamJITSIProctoringService(null, null, null, cryptorMock); + + String accessToken = examJITSIProctoringService.createPayload( + "test-app", + "Test Name", + "test-client", + "SomeRoom", + 1609459200L, + "https://test.ch", + false); + + assertEquals( + "{\"context\":{\"user\":{\"name\":\"Test Name\"}},\"iss\":\"test-app\",\"aud\":\"test-client\",\"sub\":\"https://test.ch\",\"room\":\"SomeRoom\",\"moderator\":false,\"exp\":1609459200}", + accessToken); + + accessToken = examJITSIProctoringService.createPayload( + "test-app", + "Test Name", + "test-client", + "SomeRoom", + 1609459200L, + "https://test.ch", + true); + + assertEquals( + "{\"context\":{\"user\":{\"name\":\"Test Name\"}},\"iss\":\"test-app\",\"aud\":\"test-client\",\"sub\":\"https://test.ch\",\"room\":\"SomeRoom\",\"moderator\":true,\"exp\":1609459200}", + accessToken); + } + @Test public void testCreateProctoringURL() { final Cryptor cryptorMock = Mockito.mock(Cryptor.class); @@ -36,15 +73,17 @@ public class ExamJITSIProctoringServiceTest { "test-client", "SomeRoom", "Subject", - 1609459200L) + 1609459200L, + true) .getOrThrow(); assertNotNull(data); assertEquals( "https://seb-jitsi.example.ch", data.serverURL); + assertEquals( - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwibW9kZXJhdG9yIjogdHJ1ZSwiZXhwIjoxNjA5NDU5MjAwfQ.RjqLawNlBQgECKGZFi6jfcVXEw2dbZ1p9C1xEz-0e9w", data.accessToken); }