SEBSERV-139 Jitsi Javascript API integration
This commit is contained in:
parent
59e3d29e3d
commit
0e293c7602
7 changed files with 195 additions and 61 deletions
|
@ -15,11 +15,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SEBClientProctoringConnectionData {
|
||||
|
||||
public static final String ATTR_SERVER_HOST = "serverHost";
|
||||
public static final String ATTR_SERVER_URL = "serverURL";
|
||||
public static final String ATTR_ROOM_NAME = "roomName";
|
||||
public static final String ATTR_ACCESS_TOKEN = "accessToken";
|
||||
public static final String ATTR_CONNECTION_URL = "connectionURL";
|
||||
|
||||
@JsonProperty(ATTR_SERVER_HOST)
|
||||
public final String serverHost;
|
||||
|
||||
@JsonProperty(ATTR_SERVER_URL)
|
||||
public final String serverURL;
|
||||
|
||||
|
@ -34,17 +38,23 @@ public class SEBClientProctoringConnectionData {
|
|||
|
||||
@JsonCreator
|
||||
public SEBClientProctoringConnectionData(
|
||||
@JsonProperty(ATTR_SERVER_HOST) final String serverHost,
|
||||
@JsonProperty(ATTR_SERVER_URL) final String serverURL,
|
||||
@JsonProperty(ATTR_ROOM_NAME) final String roomName,
|
||||
@JsonProperty(ATTR_ACCESS_TOKEN) final String accessToken,
|
||||
@JsonProperty(ATTR_CONNECTION_URL) final String connectionURL) {
|
||||
|
||||
this.serverHost = serverHost;
|
||||
this.serverURL = serverURL;
|
||||
this.roomName = roomName;
|
||||
this.accessToken = accessToken;
|
||||
this.connectionURL = connectionURL;
|
||||
}
|
||||
|
||||
public String getServerHost() {
|
||||
return this.serverHost;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
@ -64,7 +74,9 @@ public class SEBClientProctoringConnectionData {
|
|||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("SEBClientProctoringConnectionData [serverURL=");
|
||||
builder.append("SEBClientProctoringConnectionData [serverHost=");
|
||||
builder.append(this.serverHost);
|
||||
builder.append(", serverURL=");
|
||||
builder.append(this.serverURL);
|
||||
builder.append(", roomName=");
|
||||
builder.append(this.roomName);
|
||||
|
|
102
src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
Normal file
102
src/main/java/ch/ethz/seb/sebserver/gui/ProctoringServlet.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBClientProctoringConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.AuthorizationContextHolder;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.SEBServerAuthorizationContext;
|
||||
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ProctoringServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 3475978419653411800L;
|
||||
|
||||
public static final String SESSION_ATTR_PROCTORING_DATA = "SESSION_ATTR_PROCTORING_DATA";
|
||||
|
||||
// @formatter:off
|
||||
private static final String HTML =
|
||||
"<!DOCTYPE html>" +
|
||||
"<html>" +
|
||||
"<head>" +
|
||||
" <title></title>" +
|
||||
" <script src='https://%s/external_api.js'></script>" +
|
||||
"</head>" +
|
||||
"" +
|
||||
"<body>" +
|
||||
"<div id=\"proctoring\"></div> " +
|
||||
"</body>" +
|
||||
"<script>" +
|
||||
" const options = {\r\n" +
|
||||
" parentNode: document.querySelector('#proctoring'),\r\n" +
|
||||
" roomName: '%s',\r\n" +
|
||||
" width: 600,\r\n" +
|
||||
" height: 400,\r\n" +
|
||||
" jwt: '%s'\r\n" +
|
||||
" }\r\n" +
|
||||
" meetAPI = new JitsiMeetExternalAPI(\"%s\", options);\r\n" +
|
||||
"</script>" +
|
||||
"</html>";
|
||||
// @formatter:on
|
||||
|
||||
@Override
|
||||
protected void doGet(
|
||||
final HttpServletRequest req,
|
||||
final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
|
||||
final HttpSession httpSession = req.getSession();
|
||||
final ServletContext servletContext = httpSession.getServletContext();
|
||||
final WebApplicationContext webApplicationContext = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(servletContext);
|
||||
|
||||
final boolean authenticated = isAuthenticated(httpSession, webApplicationContext);
|
||||
if (!authenticated) {
|
||||
resp.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
return;
|
||||
}
|
||||
|
||||
final SEBClientProctoringConnectionData proctoringConnectionData =
|
||||
(SEBClientProctoringConnectionData) httpSession.getAttribute(SESSION_ATTR_PROCTORING_DATA);
|
||||
|
||||
final String accessToken = proctoringConnectionData.getAccessToken();
|
||||
final String roomName = proctoringConnectionData.roomName;
|
||||
final String server = "seb-jitsi.ethz.ch";
|
||||
final String script = String.format(HTML, server, roomName, accessToken, server);
|
||||
resp.getOutputStream().println(script);
|
||||
|
||||
}
|
||||
|
||||
private boolean isAuthenticated(
|
||||
final HttpSession httpSession,
|
||||
final WebApplicationContext webApplicationContext) {
|
||||
|
||||
final AuthorizationContextHolder authorizationContextHolder = webApplicationContext
|
||||
.getBean(AuthorizationContextHolder.class);
|
||||
final SEBServerAuthorizationContext authorizationContext = authorizationContextHolder
|
||||
.getAuthorizationContext(httpSession);
|
||||
return authorizationContext.isValid() && authorizationContext.isLoggedIn();
|
||||
}
|
||||
|
||||
}
|
|
@ -8,14 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.rap.rwt.engine.RWTServlet;
|
||||
import org.eclipse.rap.rwt.engine.RWTServletContextListener;
|
||||
|
@ -25,6 +19,7 @@ import org.springframework.beans.factory.annotation.Value;
|
|||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -63,25 +58,12 @@ public class RAPSpringConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<ProctoringServlet> servletProctoringRegistrationBean() {
|
||||
return new ServletRegistrationBean<>(new ProctoringServlet(), "/proctoring/*");
|
||||
}
|
||||
|
||||
private static class ProctoringServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 3475978419653411800L;
|
||||
|
||||
@Override
|
||||
protected void doGet(
|
||||
final HttpServletRequest req,
|
||||
final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
|
||||
System.out.println("********************************");
|
||||
resp.getOutputStream().println("Hello");
|
||||
|
||||
}
|
||||
public ServletRegistrationBean<ProctoringServlet> servletProctoringRegistrationBean(
|
||||
final ApplicationContext applicationContext) {
|
||||
|
||||
final ProctoringServlet proctoringServlet = applicationContext
|
||||
.getBean(ProctoringServlet.class);
|
||||
return new ServletRegistrationBean<>(proctoringServlet, "/proctoring/*");
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.gui.content;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Base64.Encoder;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
|
@ -33,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent;
|
|||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.gui.ProctoringServlet;
|
||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
|
||||
|
@ -276,16 +279,18 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
|
||||
}
|
||||
|
||||
private PageAction openProctorScreen(final PageAction action, final String connectionToken) {
|
||||
//
|
||||
// final ProctorDialog dialog = new ProctorDialog(action.pageContext().getParent().getShell());
|
||||
// dialog.open(EVENT_LIST_TITLE_KEY,
|
||||
// "https://seb-jitsi.ethz.ch/TestRoomABC?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsiYXZhdGFyIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9qb2huLWRvZSIsIm5hbWUiOiJEaXNwbGF5IE5hbWUiLCJlbWFpbCI6Im5hbWVAZXhhbXBsZS5jb20ifX0sImF1ZCI6InNlYi1qaXRzaSIsImlzcyI6InNlYi1qaXRzaSIsInN1YiI6Im1lZXQuaml0c2kiLCJyb29tIjoiKiJ9.SD9Zs78mMFqxS1tpalPTykYYaubIYsj_406WAOhcqxQ");
|
||||
//
|
||||
// final UrlLauncher urlLauncher = RWT.getClient().getService(UrlLauncher.class);
|
||||
// urlLauncher.openURL(
|
||||
// "https://seb-jitsi.ethz.ch/TestRoomABC?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb250ZXh0Ijp7InVzZXIiOnsiYXZhdGFyIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9qb2huLWRvZSIsIm5hbWUiOiJEaXNwbGF5IE5hbWUiLCJlbWFpbCI6Im5hbWVAZXhhbXBsZS5jb20ifX0sImF1ZCI6InNlYi1qaXRzaSIsImlzcyI6InNlYi1qaXRzaSIsInN1YiI6Im1lZXQuaml0c2kiLCJyb29tIjoiKiJ9.SD9Zs78mMFqxS1tpalPTykYYaubIYsj_406WAOhcqxQ");
|
||||
// @formatter:off
|
||||
private static final String OPEN_SINGEL_ROOM_SCRIPT =
|
||||
"var existingWin = window.open('', '%s', 'height=420,width=620,location=no,scrollbars=yes,status=no,menubar=yes,toolbar=yes,titlebar=yes');\n" +
|
||||
"if(existingWin.location.href === 'about:blank'){\n" +
|
||||
" existingWin.location.href = '%s/proctoring/%s';\n" +
|
||||
" existingWin.focus();\n" +
|
||||
"} else {\n" +
|
||||
" existingWin.focus();\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
|
||||
private PageAction openProctorScreen(final PageAction action, final String connectionToken) {
|
||||
final SEBClientProctoringConnectionData proctoringConnectionData =
|
||||
this.pageService.getRestService().getBuilder(GetProctorURLForClient.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, action.getEntityKey().modelId)
|
||||
|
@ -293,20 +298,20 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Encoder urlEncoder = Base64.getUrlEncoder().withoutPadding();
|
||||
final String roomName = urlEncoder.encodeToString(Utils.toByteArray(connectionToken));
|
||||
|
||||
RWT.getUISession().getHttpSession().setAttribute(
|
||||
ProctoringServlet.SESSION_ATTR_PROCTORING_DATA,
|
||||
proctoringConnectionData);
|
||||
|
||||
final String webserviceServerAddress = this.pageService.getAuthorizationContextHolder()
|
||||
.getWebserviceURIService()
|
||||
.getWebserviceServerAddress();
|
||||
|
||||
final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
|
||||
javaScriptExecutor.execute(
|
||||
"window.open("
|
||||
+ "'" + proctoringConnectionData.connectionURL + "',"
|
||||
+ "'proctoring',"
|
||||
+ "'height=400,width=800,location=no,scrollbars=yes,status=no,menubar=yes,toolbar=yes,titlebar=yes');");
|
||||
|
||||
// final JavaScriptExecutor javaScriptExecutor = RWT.getClient().getService(JavaScriptExecutor.class);
|
||||
// javaScriptExecutor.execute(
|
||||
// "window.open("
|
||||
// + "'http://localhost:8080/proctoring',"
|
||||
// + "'proctoring',"
|
||||
// + "'height=400,width=800,location=no,scrollbars=yes,status=no,menubar=yes,toolbar=yes,titlebar=yes');");
|
||||
|
||||
final String script = String.format(OPEN_SINGEL_ROOM_SCRIPT, roomName, webserviceServerAddress, roomName);
|
||||
javaScriptExecutor.execute(script);
|
||||
return action;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
|||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamProctoringService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||
|
||||
|
@ -45,13 +46,16 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
private static final String JITSI_ACCESS_TOKEN_PAYLOAD =
|
||||
"{\"context\":{\"user\":{\"name\":\"%s\"}},\"iss\":\"%s\",\"aud\":\"%s\",\"sub\":\"%s\",\"room\":\"%s\"%s}";
|
||||
|
||||
private final AuthorizationService authorizationService;
|
||||
private final ExamSessionService examSessionService;
|
||||
private final Cryptor cryptor;
|
||||
|
||||
protected ExamJITSIProctoringService(
|
||||
final AuthorizationService authorizationService,
|
||||
final ExamSessionService examSessionService,
|
||||
final Cryptor cryptor) {
|
||||
|
||||
this.authorizationService = authorizationService;
|
||||
this.examSessionService = examSessionService;
|
||||
this.cryptor = cryptor;
|
||||
}
|
||||
|
@ -113,7 +117,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
examProctoring.serverURL,
|
||||
examProctoring.appKey,
|
||||
examProctoring.getAppSecret(),
|
||||
clientConnection.userSessionId,
|
||||
this.authorizationService.getUserService().getCurrentUser().getUsername(),
|
||||
(server) ? "seb-server" : "seb-client",
|
||||
roomName,
|
||||
expTime)
|
||||
|
@ -154,6 +158,7 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
.append(token).toString();
|
||||
|
||||
return new SEBClientProctoringConnectionData(
|
||||
host,
|
||||
roomUrl,
|
||||
roomName,
|
||||
token,
|
||||
|
@ -166,9 +171,12 @@ public class ExamJITSIProctoringService implements ExamProctoringService {
|
|||
final String roomName) {
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
return builder.append(url)
|
||||
.append("/")
|
||||
.append(roomName)
|
||||
builder.append(url);
|
||||
if (!url.endsWith(Constants.URL_PATH_SEPARATOR)) {
|
||||
builder.append(Constants.URL_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
return builder.append(roomName)
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
@ -49,6 +51,8 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
|
|||
private final WebserviceInfo webserviceInfo;
|
||||
private final ClientConnectionDAO clientConnectionDAO;
|
||||
private final ClientInstructionDAO clientInstructionDAO;
|
||||
private final JSONMapper jsonMapper;
|
||||
|
||||
private final Map<String, ClientInstructionRecord> instructions;
|
||||
|
||||
private long lastRefresh = 0;
|
||||
|
@ -56,11 +60,13 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
|
|||
public SEBInstructionServiceImpl(
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final ClientConnectionDAO clientConnectionDAO,
|
||||
final ClientInstructionDAO clientInstructionDAO) {
|
||||
final ClientInstructionDAO clientInstructionDAO,
|
||||
final JSONMapper jsonMapper) {
|
||||
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.clientConnectionDAO = clientConnectionDAO;
|
||||
this.clientInstructionDAO = clientInstructionDAO;
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.instructions = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
|
@ -101,15 +107,13 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
|
|||
final boolean isActive = this.clientConnectionDAO
|
||||
.isActiveConnection(examId, connectionToken)
|
||||
.getOr(false);
|
||||
|
||||
if (isActive) {
|
||||
try {
|
||||
final String attributesString = new JSONMapper().writeValueAsString(attributes);
|
||||
final String attributesString = this.jsonMapper.writeValueAsString(attributes);
|
||||
this.clientInstructionDAO
|
||||
.insert(examId, type, attributesString, connectionToken, needsConfirm)
|
||||
.map(inst -> {
|
||||
this.instructions.putIfAbsent(inst.getConnectionToken(), inst);
|
||||
return inst;
|
||||
})
|
||||
.map(this::chacheInstruction)
|
||||
.onError(error -> log.error("Failed to put instruction: ", error))
|
||||
.getOrThrow();
|
||||
} catch (final Exception e) {
|
||||
|
@ -139,10 +143,10 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
|
|||
.filter(activeConnections::contains)
|
||||
.map(token -> this.clientInstructionDAO.insert(examId, type, attributesString, token, needsConfirm))
|
||||
.map(result -> result.get(
|
||||
error -> log.error("Failed to put instruction: ", error),
|
||||
error -> log.error("Failed to register instruction: ", error),
|
||||
() -> null))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(inst -> this.instructions.putIfAbsent(inst.getConnectionToken(), inst));
|
||||
.forEach(this::chacheInstruction);
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -229,4 +233,24 @@ public class SEBInstructionServiceImpl implements SEBInstructionService {
|
|||
.forEach(inst -> this.instructions.putIfAbsent(inst.getConnectionToken(), inst)));
|
||||
}
|
||||
|
||||
private ClientInstructionRecord chacheInstruction(final ClientInstructionRecord instruction) {
|
||||
final String connectionToken = instruction.getConnectionToken();
|
||||
if (this.instructions.containsKey(connectionToken)) {
|
||||
// check if previous instruction is still valid
|
||||
final ClientInstructionRecord clientInstructionRecord = this.instructions.get(connectionToken);
|
||||
if (BooleanUtils.toBoolean(BooleanUtils.toBooleanObject(clientInstructionRecord.getNeedsConfirmation()))) {
|
||||
// check if time is out
|
||||
final long now = DateTime.now(DateTimeZone.UTC).getMillis();
|
||||
final Long timestamp = clientInstructionRecord.getTimestamp();
|
||||
if (timestamp != null && now - timestamp > Constants.MINUTE_IN_MILLIS) {
|
||||
// remove old instruction and add new one
|
||||
this.instructions.put(connectionToken, instruction);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.instructions.put(connectionToken, instruction);
|
||||
}
|
||||
return instruction;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,10 @@ public class ExamJITSIProctoringServiceTest {
|
|||
|
||||
@Test
|
||||
public void testCreateProctoringURL() {
|
||||
Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
||||
final Cryptor cryptorMock = Mockito.mock(Cryptor.class);
|
||||
Mockito.when(cryptorMock.decrypt(Mockito.any())).thenReturn("fbvgeghergrgrthrehreg123");
|
||||
final ExamJITSIProctoringService examJITSIProctoringService = new ExamJITSIProctoringService(null, cryptorMock);
|
||||
final ExamJITSIProctoringService examJITSIProctoringService =
|
||||
new ExamJITSIProctoringService(null, null, cryptorMock);
|
||||
final SEBClientProctoringConnectionData data = examJITSIProctoringService.createProctoringConnectionData(
|
||||
"https://seb-jitsi.example.ch",
|
||||
"test-app",
|
||||
|
|
Loading…
Add table
Reference in a new issue