SEBSERV-139 added moderator flag to the jitsi meet room token

This commit is contained in:
anhefti 2021-02-15 16:29:22 +01:00
parent b6e9e55106
commit a6a9988cbe
3 changed files with 100 additions and 23 deletions

View file

@ -16,10 +16,23 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
public interface ExamProctoringService { public interface ExamProctoringService {
/** Get the proctoring server type of the specific implementation
*
* @return the proctoring service type of the specific implementation */
ProctoringServerType getType(); 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<Boolean> testExamProctoring(final ProctoringSettings examProctoring); Result<Boolean> 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<SEBProctoringConnectionData> createProctorPublicRoomConnection( Result<SEBProctoringConnectionData> createProctorPublicRoomConnection(
final ProctoringSettings proctoringSettings, final ProctoringSettings proctoringSettings,
final String roomName, final String roomName,
@ -59,7 +72,8 @@ public interface ExamProctoringService {
final String clientKey, final String clientKey,
final String roomName, final String roomName,
final String subject, final String subject,
final Long expTime); final Long expTime,
final boolean moderator);
Result<String> createClientAccessToken( Result<String> createClientAccessToken(
final ProctoringSettings proctoringSettings, final ProctoringSettings proctoringSettings,

View file

@ -46,7 +46,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
private static final String JITSI_ACCESS_TOKEN_PAYLOAD = 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 RemoteProctoringRoomDAO remoteProctoringRoomDAO;
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
@ -93,7 +93,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-server", "seb-server",
roomName, roomName,
subject, subject,
forExam(proctoringSettings)) forExam(proctoringSettings),
true)
.getOrThrow(); .getOrThrow();
}); });
} }
@ -131,7 +132,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client", "seb-client",
roomName, roomName,
connection.userSessionId, connection.userSessionId,
forExam(proctoringSettings)) forExam(proctoringSettings),
false)
.getOrThrow(); .getOrThrow();
}); });
} }
@ -158,7 +160,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client", "seb-client",
roomName, roomName,
subject, subject,
forExam(proctoringSettings)) forExam(proctoringSettings),
false)
.getOrThrow(); .getOrThrow();
}); });
} }
@ -209,7 +212,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client", "seb-client",
roomName, roomName,
subject, subject,
expTime) expTime,
false)
.getOrThrow(); .getOrThrow();
}); });
@ -226,7 +230,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
final String clientKey, final String clientKey,
final String roomName, final String roomName,
final String subject, final String subject,
final Long expTime) { final Long expTime,
final boolean moderator) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
@ -242,7 +247,8 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
clientKey, clientKey,
roomName, roomName,
expTime, expTime,
host); host,
moderator);
return new SEBProctoringConnectionData( return new SEBProctoringConnectionData(
proctoringServerType, proctoringServerType,
@ -279,34 +285,27 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
"seb-client", "seb-client",
roomName, roomName,
forExam(proctoringSettings), forExam(proctoringSettings),
host); host,
false);
}); });
} }
private String internalCreateAccessToken( protected String internalCreateAccessToken(
final String appKey, final String appKey,
final CharSequence appSecret, final CharSequence appSecret,
final String clientName, final String clientName,
final String clientKey, final String clientKey,
final String roomName, final String roomName,
final Long expTime, final Long expTime,
final String host) throws NoSuchAlgorithmException, InvalidKeyException { final String host,
final boolean moderator) throws NoSuchAlgorithmException, InvalidKeyException {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding(); final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
final String jwtHeaderPart = urlEncoder final String jwtHeaderPart = urlEncoder
.encodeToString(JITSI_ACCESS_TOKEN_HEADER.getBytes(StandardCharsets.UTF_8)); .encodeToString(JITSI_ACCESS_TOKEN_HEADER.getBytes(StandardCharsets.UTF_8));
final String jwtPayload = String.format( final String jwtPayload = createPayload(appKey, clientName, clientKey, roomName, expTime, host, moderator);
JITSI_ACCESS_TOKEN_PAYLOAD.replaceAll(" ", "").replaceAll("\n", ""),
clientName,
appKey,
clientKey,
host,
roomName,
(expTime != null)
? String.format(",\"exp\":%s", String.valueOf(expTime))
: "");
final String jwtPayloadPart = urlEncoder final String jwtPayloadPart = urlEncoder
.encodeToString(jwtPayload.getBytes(StandardCharsets.UTF_8)); .encodeToString(jwtPayload.getBytes(StandardCharsets.UTF_8));
final String message = jwtHeaderPart + "." + jwtPayloadPart; final String message = jwtHeaderPart + "." + jwtPayloadPart;
@ -324,6 +323,31 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
return builder.toString(); 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) { private long forExam(final ProctoringSettings examProctoring) {
if (examProctoring.examId == null) { if (examProctoring.examId == null) {
throw new IllegalStateException("Missing exam identifier from ExamProctoring data"); throw new IllegalStateException("Missing exam identifier from ExamProctoring data");

View file

@ -11,6 +11,9 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -20,6 +23,40 @@ import ch.ethz.seb.sebserver.gbl.util.Cryptor;
public class ExamJITSIProctoringServiceTest { 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 @Test
public void testCreateProctoringURL() { public void testCreateProctoringURL() {
final Cryptor cryptorMock = Mockito.mock(Cryptor.class); final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
@ -36,15 +73,17 @@ public class ExamJITSIProctoringServiceTest {
"test-client", "test-client",
"SomeRoom", "SomeRoom",
"Subject", "Subject",
1609459200L) 1609459200L,
true)
.getOrThrow(); .getOrThrow();
assertNotNull(data); assertNotNull(data);
assertEquals( assertEquals(
"https://seb-jitsi.example.ch", "https://seb-jitsi.example.ch",
data.serverURL); data.serverURL);
assertEquals( assertEquals(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwiZXhwIjoxNjA5NDU5MjAwfQ.4ovqUkG6jrLvkDEZNdhbtFI_DFLDFsM2eBJHhcYq7a4", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsibmFtZSI6IlRlc3QgTmFtZSJ9fSwiaXNzIjoidGVzdC1hcHAiLCJhdWQiOiJ0ZXN0LWNsaWVudCIsInN1YiI6InNlYi1qaXRzaS5leGFtcGxlLmNoIiwicm9vbSI6IlNvbWVSb29tIiwibW9kZXJhdG9yIjogdHJ1ZSwiZXhwIjoxNjA5NDU5MjAwfQ.RjqLawNlBQgECKGZFi6jfcVXEw2dbZ1p9C1xEz-0e9w",
data.accessToken); data.accessToken);
} }