Implement SDKToken for Zoom proctoring with SEB client for iOS and MacOS
This commit is contained in:
parent
9a9ad6e840
commit
9d27dbfbcf
12 changed files with 143 additions and 10 deletions
|
@ -23,6 +23,7 @@ public class ProctoringRoomConnection {
|
|||
public static final String ATTR_ROOM_NAME = "roomName";
|
||||
public static final String ATTR_SUBJECT = "subject";
|
||||
public static final String ATTR_ACCESS_TOKEN = "accessToken";
|
||||
public static final String ATTR_SDK_TOKEN = "sdkToken";
|
||||
public static final String ATTR_CONNECTION_URL = "connectionURL";
|
||||
public static final String ATTR_USER_NAME = "userName";
|
||||
public static final String ATTR_ROOM_KEY = "roomKey";
|
||||
|
@ -50,6 +51,9 @@ public class ProctoringRoomConnection {
|
|||
@JsonProperty(ATTR_ACCESS_TOKEN)
|
||||
public final CharSequence accessToken;
|
||||
|
||||
@JsonProperty(ATTR_SDK_TOKEN)
|
||||
public final CharSequence sdkToken;
|
||||
|
||||
@JsonProperty(ATTR_ROOM_KEY)
|
||||
public final CharSequence roomKey;
|
||||
|
||||
|
@ -71,6 +75,7 @@ public class ProctoringRoomConnection {
|
|||
@JsonProperty(ATTR_ROOM_NAME) final String roomName,
|
||||
@JsonProperty(ATTR_SUBJECT) final String subject,
|
||||
@JsonProperty(ATTR_ACCESS_TOKEN) final CharSequence accessToken,
|
||||
@JsonProperty(ATTR_SDK_TOKEN) final CharSequence sdkToken,
|
||||
@JsonProperty(ATTR_ROOM_KEY) final CharSequence roomKey,
|
||||
@JsonProperty(ATTR_API_KEY) final CharSequence apiKey,
|
||||
@JsonProperty(ATTR_MEETING_ID) final String meetingId,
|
||||
|
@ -83,6 +88,7 @@ public class ProctoringRoomConnection {
|
|||
this.roomName = roomName;
|
||||
this.subject = subject;
|
||||
this.accessToken = accessToken;
|
||||
this.sdkToken = sdkToken;
|
||||
this.roomKey = roomKey;
|
||||
this.apiKey = apiKey;
|
||||
this.meetingId = meetingId;
|
||||
|
@ -105,6 +111,10 @@ public class ProctoringRoomConnection {
|
|||
return this.accessToken;
|
||||
}
|
||||
|
||||
public CharSequence getSdkToken() {
|
||||
return this.sdkToken;
|
||||
}
|
||||
|
||||
public CharSequence getRoomKey() {
|
||||
return this.roomKey;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ public class ProctoringServiceSettings implements Entity {
|
|||
public static final String ATTR_SERVER_URL = "serverURL";
|
||||
public static final String ATTR_APP_KEY = "appKey";
|
||||
public static final String ATTR_APP_SECRET = "appSecret";
|
||||
public static final String ATTR_SDK_KEY = "sdkKey";
|
||||
public static final String ATTR_SDK_SECRET = "sdkSecret";
|
||||
public static final String ATTR_COLLECTING_ROOM_SIZE = "collectingRoomSize";
|
||||
public static final String ATTR_ENABLED_FEATURES = "enabledFeatures";
|
||||
public static final String ATTR_COLLECT_ALL_ROOM_NAME = "collectAllRoomName";
|
||||
|
@ -67,6 +69,12 @@ public class ProctoringServiceSettings implements Entity {
|
|||
@JsonProperty(ATTR_APP_SECRET)
|
||||
public final CharSequence appSecret;
|
||||
|
||||
@JsonProperty(ATTR_SDK_KEY)
|
||||
public final String sdkKey;
|
||||
|
||||
@JsonProperty(ATTR_SDK_SECRET)
|
||||
public final CharSequence sdkSecret;
|
||||
|
||||
@JsonProperty(ATTR_COLLECTING_ROOM_SIZE)
|
||||
public final Integer collectingRoomSize;
|
||||
|
||||
|
@ -86,7 +94,9 @@ public class ProctoringServiceSettings implements Entity {
|
|||
@JsonProperty(ATTR_ENABLED_FEATURES) final EnumSet<ProctoringFeature> enabledFeatures,
|
||||
@JsonProperty(ATTR_SERVICE_IN_USE) final Boolean serviceInUse,
|
||||
@JsonProperty(ATTR_APP_KEY) final String appKey,
|
||||
@JsonProperty(ATTR_APP_SECRET) final CharSequence appSecret) {
|
||||
@JsonProperty(ATTR_APP_SECRET) final CharSequence appSecret,
|
||||
@JsonProperty(ATTR_SDK_KEY) final String sdkKey,
|
||||
@JsonProperty(ATTR_SDK_SECRET) final CharSequence sdkSecret) {
|
||||
|
||||
this.examId = examId;
|
||||
this.enableProctoring = BooleanUtils.isTrue(enableProctoring);
|
||||
|
@ -97,6 +107,8 @@ public class ProctoringServiceSettings implements Entity {
|
|||
this.serviceInUse = serviceInUse;
|
||||
this.appKey = appKey;
|
||||
this.appSecret = appSecret;
|
||||
this.sdkKey = sdkKey;
|
||||
this.sdkSecret = sdkSecret;
|
||||
|
||||
}
|
||||
|
||||
|
@ -147,6 +159,14 @@ public class ProctoringServiceSettings implements Entity {
|
|||
return this.appSecret;
|
||||
}
|
||||
|
||||
public String getSdkKey() {
|
||||
return this.sdkKey;
|
||||
}
|
||||
|
||||
public CharSequence getSdkSecret() {
|
||||
return this.sdkSecret;
|
||||
}
|
||||
|
||||
public Boolean getServiceInUse() {
|
||||
return this.serviceInUse;
|
||||
}
|
||||
|
@ -193,7 +213,11 @@ public class ProctoringServiceSettings implements Entity {
|
|||
builder.append(", appKey=");
|
||||
builder.append(this.appKey);
|
||||
builder.append(", appSecret=");
|
||||
builder.append(this.appSecret);
|
||||
builder.append("--");
|
||||
builder.append(", sdkKey=");
|
||||
builder.append(this.sdkKey);
|
||||
builder.append(", sdkSecret=");
|
||||
builder.append("--");
|
||||
builder.append(", collectingRoomSize=");
|
||||
builder.append(this.collectingRoomSize);
|
||||
builder.append(", enabledFeatures=");
|
||||
|
|
|
@ -54,6 +54,7 @@ public final class ClientInstruction {
|
|||
public static final String ZOOM_USER_NAME = "zoomUserName";
|
||||
public static final String ZOOM_API_KEY = "zoomAPIKey";
|
||||
public static final String ZOOM_TOKEN = "zoomToken";
|
||||
public static final String ZOOM_SDK_TOKEN = "zoomSDKToken";
|
||||
public static final String ZOOM_MEETING_KEY = "zoomMeetingKey";
|
||||
public static final String ZOOM_RECEIVE_AUDIO = "zoomReceiveAudio";
|
||||
public static final String ZOOM_RECEIVE_VIDEO = "zoomReceiveVideo";
|
||||
|
|
|
@ -72,6 +72,11 @@ public class ExamProctoringSettings {
|
|||
new LocTextKey("sebserver.exam.proctoring.form.appkey");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_SECRET =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.secret");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_SDKKEY =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.sdkkey");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_SDKSECRET =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.sdksecret");
|
||||
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_FEATURES =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.features");
|
||||
|
||||
|
@ -155,7 +160,9 @@ public class ExamProctoringSettings {
|
|||
featureFlags,
|
||||
false,
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_KEY),
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_SECRET));
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_SECRET),
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_SDK_KEY),
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_SDK_SECRET));
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to get settings from form: ", e);
|
||||
|
@ -225,6 +232,8 @@ public class ExamProctoringSettings {
|
|||
.copyOf(content)
|
||||
.clearEntityKeys();
|
||||
|
||||
final boolean isZoom = proctoringSettings.serverType == ProctoringServerType.ZOOM;
|
||||
|
||||
final FormHandle<ProctoringServiceSettings> formHandle = this.pageService.formBuilder(
|
||||
formContext)
|
||||
.withDefaultSpanInput(5)
|
||||
|
@ -249,7 +258,8 @@ public class ExamProctoringSettings {
|
|||
ProctoringServiceSettings.ATTR_SERVER_TYPE,
|
||||
SEB_PROCTORING_FORM_TYPE,
|
||||
proctoringSettings.serverType.name(),
|
||||
resourceService::examProctoringTypeResources))
|
||||
resourceService::examProctoringTypeResources)
|
||||
.withSelectionListener(this::serviceSelection))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ProctoringServiceSettings.ATTR_SERVER_URL,
|
||||
|
@ -269,6 +279,21 @@ public class ExamProctoringSettings {
|
|||
? String.valueOf(proctoringSettings.appSecret)
|
||||
: null))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ProctoringServiceSettings.ATTR_SDK_KEY,
|
||||
SEB_PROCTORING_FORM_SDKKEY,
|
||||
proctoringSettings.sdkKey)
|
||||
.visibleIf(isZoom))
|
||||
.withEmptyCellSeparation(false)
|
||||
|
||||
.addField(FormBuilder.password(
|
||||
ProctoringServiceSettings.ATTR_SDK_SECRET,
|
||||
SEB_PROCTORING_FORM_SDKSECRET,
|
||||
(proctoringSettings.sdkSecret != null)
|
||||
? String.valueOf(proctoringSettings.sdkSecret)
|
||||
: null)
|
||||
.visibleIf(isZoom))
|
||||
|
||||
.withDefaultSpanInput(1)
|
||||
.addField(FormBuilder.text(
|
||||
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
|
||||
|
@ -294,6 +319,14 @@ public class ExamProctoringSettings {
|
|||
|
||||
return () -> formHandle;
|
||||
}
|
||||
|
||||
private void serviceSelection(final Form form) {
|
||||
final boolean isZoom = ProctoringServerType.ZOOM.name()
|
||||
.equals(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE));
|
||||
|
||||
form.setFieldVisible(isZoom, ProctoringServiceSettings.ATTR_SDK_KEY);
|
||||
form.setFieldVisible(isZoom, ProctoringServiceSettings.ATTR_SDK_SECRET);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -213,7 +213,9 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
getEnabledFeatures(mapping),
|
||||
this.remoteProctoringRoomDAO.isServiceInUse(examId).getOr(true),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET));
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -263,6 +265,22 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
.getOrThrow()
|
||||
.toString());
|
||||
|
||||
if (StringUtils.isNotBlank(proctoringServiceSettings.appKey)) {
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_SDK_KEY,
|
||||
proctoringServiceSettings.sdkKey);
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_SDK_SECRET,
|
||||
this.cryptor.encrypt(proctoringServiceSettings.sdkSecret)
|
||||
.getOrThrow()
|
||||
.toString());
|
||||
}
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
|
|
|
@ -378,6 +378,7 @@ public class JitsiProctoringService implements ExamProctoringService {
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
clientName);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -214,6 +214,9 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
attributes.put(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_TOKEN,
|
||||
String.valueOf(proctoringConnection.accessToken));
|
||||
attributes.put(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_SDK_TOKEN,
|
||||
String.valueOf(proctoringConnection.sdkToken));
|
||||
if (StringUtils.isNotBlank(proctoringConnection.apiKey)) {
|
||||
attributes.put(
|
||||
ClientInstruction.SEB_INSTRUCTION_ATTRIBUTES.SEB_PROCTORING.ZOOM_API_KEY,
|
||||
|
@ -262,6 +265,19 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
String.valueOf(additionalZoomRoomData.meeting_id),
|
||||
true);
|
||||
|
||||
String sdkJWT = null;
|
||||
if (StringUtils.isNotBlank(proctoringSettings.sdkKey)) {
|
||||
|
||||
final ClientCredentials sdkCredentials = new ClientCredentials(
|
||||
proctoringSettings.sdkKey,
|
||||
proctoringSettings.sdkSecret,
|
||||
remoteProctoringRoom.joinKey);
|
||||
|
||||
sdkJWT = this.createJWTForSDKAccess(
|
||||
sdkCredentials,
|
||||
String.valueOf(additionalZoomRoomData.meeting_id));
|
||||
}
|
||||
|
||||
return new ProctoringRoomConnection(
|
||||
ProctoringServerType.ZOOM,
|
||||
null,
|
||||
|
@ -270,6 +286,7 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
roomName,
|
||||
subject,
|
||||
jwt,
|
||||
sdkJWT,
|
||||
credentials.accessToken,
|
||||
credentials.clientId,
|
||||
String.valueOf(additionalZoomRoomData.meeting_id),
|
||||
|
@ -308,6 +325,19 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
.getConnectionData(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
String sdkJWT = null;
|
||||
if (StringUtils.isNotBlank(proctoringSettings.sdkKey)) {
|
||||
|
||||
final ClientCredentials sdkCredentials = new ClientCredentials(
|
||||
proctoringSettings.sdkKey,
|
||||
proctoringSettings.sdkSecret,
|
||||
remoteProctoringRoom.joinKey);
|
||||
|
||||
sdkJWT = this.createJWTForSDKAccess(
|
||||
sdkCredentials,
|
||||
String.valueOf(additionalZoomRoomData.meeting_id));
|
||||
}
|
||||
|
||||
return new ProctoringRoomConnection(
|
||||
ProctoringServerType.ZOOM,
|
||||
connectionToken,
|
||||
|
@ -316,6 +346,7 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
roomName,
|
||||
subject,
|
||||
jwt,
|
||||
sdkJWT,
|
||||
credentials.accessToken,
|
||||
credentials.clientId,
|
||||
String.valueOf(additionalZoomRoomData.meeting_id),
|
||||
|
@ -613,6 +644,13 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
}
|
||||
}
|
||||
|
||||
private String createJWTForSDKAccess(
|
||||
final ClientCredentials sdkCredentials,
|
||||
final String meetingId) {
|
||||
|
||||
return createJWTForMeetingAccess(sdkCredentials, meetingId, false);
|
||||
}
|
||||
|
||||
private String createJWTForMeetingAccess(
|
||||
final ClientCredentials credentials,
|
||||
final String meetingId,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
server.address=localhost
|
||||
server.port=8090
|
||||
server.port=8080
|
||||
|
||||
sebserver.gui.http.external.scheme=http
|
||||
sebserver.gui.entrypoint=/gui
|
||||
sebserver.gui.webservice.protocol=http
|
||||
sebserver.gui.webservice.address=localhost
|
||||
sebserver.gui.webservice.port=8090
|
||||
sebserver.gui.webservice.port=8080
|
||||
sebserver.gui.webservice.apipath=/admin-api/v1
|
||||
# defines the polling interval that is used to poll the webservice for client connection data on a monitored exam page
|
||||
sebserver.gui.webservice.poll-interval=1000
|
||||
|
|
|
@ -658,10 +658,14 @@ sebserver.exam.proctoring.form.url=Server URL
|
|||
sebserver.exam.proctoring.form.url.tooltip=The proctoring server URL
|
||||
sebserver.exam.proctoring.form.collectingRoomSize=Collecting Room Size
|
||||
sebserver.exam.proctoring.form.collectingRoomSize.tooltip=The size of proctor rooms to collect connecting SEB clients into.
|
||||
sebserver.exam.proctoring.form.appkey=Application Key
|
||||
sebserver.exam.proctoring.form.appkey=App Key
|
||||
sebserver.exam.proctoring.form.appkey.tooltip=The application key of the proctoring service server
|
||||
sebserver.exam.proctoring.form.secret=Secret
|
||||
sebserver.exam.proctoring.form.secret=App Secret
|
||||
sebserver.exam.proctoring.form.secret.tooltip=The secret used to access the proctoring service
|
||||
sebserver.exam.proctoring.form.sdkkey=SDK Key (Zoom)
|
||||
sebserver.exam.proctoring.form.sdkkey.tooltip=The SDK key and secret are used for live proctoring with SEB clients for iOS and/or MacOS
|
||||
sebserver.exam.proctoring.form.sdksecret=SDK Secret (Zoom)
|
||||
sebserver.exam.proctoring.form.sdksecret.tooltip=The SDK key and secret are used for live proctoring with SEB clients for iOS and/or MacOS
|
||||
sebserver.exam.proctoring.form.features=Enabled Features
|
||||
sebserver.exam.proctoring.form.features.TOWN_HALL=Town-Hall Room
|
||||
sebserver.exam.proctoring.form.features.ONE_TO_ONE=One to One Room
|
||||
|
|
|
@ -37,6 +37,7 @@ public class JitsiWindowScriptResolverTest {
|
|||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
null,
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
|
@ -53,6 +54,7 @@ public class JitsiWindowScriptResolverTest {
|
|||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
null,
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
|
|
|
@ -37,6 +37,7 @@ public class ZoomWindowScriptResolverTest {
|
|||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
"SDK_TOKEN",
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
|
@ -53,6 +54,7 @@ public class ZoomWindowScriptResolverTest {
|
|||
"ROOM",
|
||||
"SUBJECT",
|
||||
"ACCESS_TOKEN",
|
||||
"SDK_TOKEN",
|
||||
"API_KEY",
|
||||
"ROOM_KEY",
|
||||
"MEETING_ID",
|
||||
|
|
|
@ -63,7 +63,7 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT
|
|||
2L,
|
||||
new ProctoringServiceSettings(
|
||||
2L, true, ProctoringServerType.JITSI_MEET, "http://jitsi.ch", 1, null, false,
|
||||
"app-key", "app.secret"));
|
||||
"app-key", "app.secret", "sdk-key", "sdk.secret"));
|
||||
|
||||
assertTrue(this.examAdminService.isProctoringEnabled(2L).get());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue