SEBWIN-494: Fixed issue with sensitive proctoring data.
This commit is contained in:
parent
d141b747eb
commit
ee2133c0c2
6 changed files with 187 additions and 127 deletions
|
@ -6,46 +6,59 @@
|
|||
<div id="placeholder" />
|
||||
<script src='https://meet.jit.si/external_api.js'></script>
|
||||
<script type="text/javascript">
|
||||
var configOverwrite = {
|
||||
disableProfile: true,
|
||||
startAudioOnly: '%_AUDIO_ONLY_%',
|
||||
startWithAudioMuted: '%_AUDIO_MUTED_%',
|
||||
startWithVideoMuted: '%_VIDEO_MUTED_%'
|
||||
};
|
||||
var interfaceOverwrite = {
|
||||
JITSI_WATERMARK_LINK: '',
|
||||
SHOW_JITSI_WATERMARK: false,
|
||||
TOOLBAR_BUTTONS: [
|
||||
'microphone', 'camera', '%%_ALLOW_CLOSED_CAPTIONS_%%', /*'desktop',*/ 'embedmeeting', 'fullscreen',
|
||||
'fodeviceselection', /*'hangup',*/ 'profile', '%%_ALLOW_CHAT_%%', '%%_ALLOW_RECORDING_%%',
|
||||
'livestreaming', 'etherpad', /*'sharedvideo',*/ 'settings', '%%_ALLOW_RAISE_HAND_%%',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'%%_ALLOW_TILE_VIEW_%%', 'select-background', 'download', 'help', /*'mute-everyone',*/ 'mute-video-everyone', 'security'
|
||||
]
|
||||
};
|
||||
var options = {
|
||||
configOverwrite: configOverwrite,
|
||||
height: "100%",
|
||||
interfaceConfigOverwrite: interfaceOverwrite,
|
||||
jwt: "%%_TOKEN_%%",
|
||||
parentNode: document.querySelector('#placeholder'),
|
||||
roomName: "%%_ROOM_NAME_%%",
|
||||
width: "100%"
|
||||
};
|
||||
var api = new JitsiMeetExternalAPI("%%_DOMAIN_%%", options);
|
||||
var api;
|
||||
|
||||
api.addListener('audioMuteStatusChanged', args => {
|
||||
if (args.muted) {
|
||||
api.executeCommand('toggleAudio');
|
||||
}
|
||||
});
|
||||
api.addListener('videoMuteStatusChanged', args => {
|
||||
if (args.muted) {
|
||||
api.executeCommand('toggleVideo');
|
||||
}
|
||||
});
|
||||
function startMeeting(credentials) {
|
||||
var configOverwrite = {
|
||||
disableProfile: true,
|
||||
startAudioOnly: '%_AUDIO_ONLY_%',
|
||||
startWithAudioMuted: '%_AUDIO_MUTED_%',
|
||||
startWithVideoMuted: '%_VIDEO_MUTED_%'
|
||||
};
|
||||
var interfaceOverwrite = {
|
||||
JITSI_WATERMARK_LINK: '',
|
||||
SHOW_JITSI_WATERMARK: false,
|
||||
TOOLBAR_BUTTONS: [
|
||||
'microphone', 'camera', '%%_ALLOW_CLOSED_CAPTIONS_%%', /*'desktop',*/ 'embedmeeting', 'fullscreen',
|
||||
'fodeviceselection', /*'hangup',*/ 'profile', '%%_ALLOW_CHAT_%%', '%%_ALLOW_RECORDING_%%',
|
||||
'livestreaming', 'etherpad', /*'sharedvideo',*/ 'settings', '%%_ALLOW_RAISE_HAND_%%',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'%%_ALLOW_TILE_VIEW_%%', 'select-background', 'download', 'help', /*'mute-everyone',*/ 'mute-video-everyone', 'security'
|
||||
]
|
||||
};
|
||||
var options = {
|
||||
configOverwrite: configOverwrite,
|
||||
height: '100%',
|
||||
interfaceConfigOverwrite: interfaceOverwrite,
|
||||
jwt: credentials.token,
|
||||
parentNode: document.querySelector('#placeholder'),
|
||||
roomName: credentials.roomName,
|
||||
width: '100%'
|
||||
};
|
||||
|
||||
api = new JitsiMeetExternalAPI(credentials.domain, options);
|
||||
api.addListener('audioMuteStatusChanged', args => {
|
||||
if (args.muted) {
|
||||
api.executeCommand('toggleAudio');
|
||||
}
|
||||
});
|
||||
api.addListener('videoMuteStatusChanged', args => {
|
||||
if (args.muted) {
|
||||
api.executeCommand('toggleVideo');
|
||||
}
|
||||
});
|
||||
|
||||
api.executeCommand("subject", "%%_SUBJECT_%%");
|
||||
api.executeCommand('subject', credentials.subject);
|
||||
}
|
||||
|
||||
function webMessageReceived(args) {
|
||||
if ('credentials' in args.data) {
|
||||
startMeeting(args.data.credentials);
|
||||
}
|
||||
}
|
||||
|
||||
window.chrome.webview.addEventListener('message', webMessageReceived);
|
||||
window.chrome.webview.postMessage('credentials');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Proctoring;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
|
||||
|
||||
|
@ -17,12 +19,15 @@ namespace SafeExamBrowser.Proctoring
|
|||
internal class ProctoringControl : WebView2, IProctoringControl
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly ProctoringSettings settings;
|
||||
|
||||
public event FullScreenChangedEventHandler FullScreenChanged;
|
||||
|
||||
internal ProctoringControl(ILogger logger)
|
||||
internal ProctoringControl(ILogger logger, ProctoringSettings settings)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
|
||||
CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted;
|
||||
}
|
||||
|
||||
|
@ -35,6 +40,7 @@ namespace SafeExamBrowser.Proctoring
|
|||
CoreWebView2.Settings.IsStatusBarEnabled = false;
|
||||
CoreWebView2.ContainsFullScreenElementChanged += CoreWebView2_ContainsFullScreenElementChanged;
|
||||
CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
|
||||
CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
|
||||
logger.Info("Successfully initialized.");
|
||||
}
|
||||
else
|
||||
|
@ -61,5 +67,47 @@ namespace SafeExamBrowser.Proctoring
|
|||
logger.Info($"Denied access to {e.PermissionKind}.");
|
||||
}
|
||||
}
|
||||
|
||||
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
|
||||
{
|
||||
var message = e.TryGetWebMessageAsString();
|
||||
|
||||
logger.Debug($"Received web message '{message}'.");
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case "credentials":
|
||||
SendCredentials();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendCredentials()
|
||||
{
|
||||
var message = new JObject();
|
||||
var credentials = new JObject();
|
||||
|
||||
if (settings.JitsiMeet.Enabled)
|
||||
{
|
||||
credentials.Add(new JProperty("domain", settings.JitsiMeet.ServerUrl));
|
||||
credentials.Add(new JProperty("roomName", settings.JitsiMeet.RoomName));
|
||||
credentials.Add(new JProperty("subject", settings.JitsiMeet.ShowMeetingName ? settings.JitsiMeet.Subject : ""));
|
||||
credentials.Add(new JProperty("token", settings.JitsiMeet.Token));
|
||||
}
|
||||
else if (settings.Zoom.Enabled)
|
||||
{
|
||||
credentials.Add(new JProperty("apiKey", settings.Zoom.ApiKey));
|
||||
credentials.Add(new JProperty("apiSecret", settings.Zoom.ApiSecret));
|
||||
credentials.Add(new JProperty("meetingNumber", settings.Zoom.MeetingNumber));
|
||||
credentials.Add(new JProperty("password", settings.Zoom.Password));
|
||||
credentials.Add(new JProperty("signature", settings.Zoom.Signature));
|
||||
credentials.Add(new JProperty("userName", settings.Zoom.UserName));
|
||||
}
|
||||
|
||||
message.Add("credentials", credentials);
|
||||
logger.Debug("Sending credentials to proctoring client.");
|
||||
|
||||
CoreWebView2.PostWebMessageAsJson(message.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace SafeExamBrowser.Proctoring
|
|||
filePath = Path.Combine(appConfig.TemporaryDirectory, $"{Path.GetRandomFileName()}_index.html");
|
||||
fileSystem.Save(content, filePath);
|
||||
|
||||
control = new ProctoringControl(logger.CloneFor(nameof(ProctoringControl)));
|
||||
control = new ProctoringControl(logger.CloneFor(nameof(ProctoringControl)), settings);
|
||||
control.CreationProperties = new CoreWebView2CreationProperties { UserDataFolder = appConfig.TemporaryDirectory };
|
||||
control.EnsureCoreWebView2Async().ContinueWith(_ =>
|
||||
{
|
||||
|
@ -253,10 +253,6 @@ namespace SafeExamBrowser.Proctoring
|
|||
html = html.Replace("%%_ALLOW_TILE_VIEW", settings.JitsiMeet.AllowTileView ? "tileview" : "");
|
||||
html = html.Replace("'%_AUDIO_MUTED_%'", settings.JitsiMeet.AudioMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
|
||||
html = html.Replace("'%_AUDIO_ONLY_%'", settings.JitsiMeet.AudioOnly ? "true" : "false");
|
||||
html = html.Replace("%%_SUBJECT_%%", settings.JitsiMeet.ShowMeetingName ? settings.JitsiMeet.Subject : " ");
|
||||
html = html.Replace("%%_DOMAIN_%%", settings.JitsiMeet.ServerUrl);
|
||||
html = html.Replace("%%_ROOM_NAME_%%", settings.JitsiMeet.RoomName);
|
||||
html = html.Replace("%%_TOKEN_%%", settings.JitsiMeet.Token);
|
||||
html = html.Replace("'%_VIDEO_MUTED_%'", settings.JitsiMeet.VideoMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
|
||||
}
|
||||
else if (settings.Zoom.Enabled)
|
||||
|
@ -264,13 +260,7 @@ namespace SafeExamBrowser.Proctoring
|
|||
html = html.Replace("'%_ALLOW_CHAT_%'", settings.Zoom.AllowChat ? "true" : "false");
|
||||
html = html.Replace("'%_ALLOW_CLOSED_CAPTIONS_%'", settings.Zoom.AllowClosedCaptions ? "true" : "false");
|
||||
html = html.Replace("'%_ALLOW_RAISE_HAND_%'", settings.Zoom.AllowRaiseHand ? "true" : "false");
|
||||
html = html.Replace("%%_API_KEY_%%", settings.Zoom.ApiKey);
|
||||
html = html.Replace("%%_API_SECRET_%%", settings.Zoom.ApiSecret);
|
||||
html = html.Replace("'%_AUDIO_MUTED_%'", settings.Zoom.AudioMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
|
||||
html = html.Replace("%%_MEETING_NUMBER_%%", settings.Zoom.MeetingNumber);
|
||||
html = html.Replace("%%_PASSWORD_%%", settings.Zoom.Password);
|
||||
html = html.Replace("%%_SIGNATURE_%%", settings.Zoom.Signature);
|
||||
html = html.Replace("%%_USER_NAME_%%", settings.Zoom.UserName);
|
||||
html = html.Replace("'%_VIDEO_MUTED_%'", settings.Zoom.VideoMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
<Reference Include="Microsoft.Web.WebView2.Wpf, Version=1.0.864.35, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.864.35\lib\net45\Microsoft.Web.WebView2.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
|
@ -116,7 +119,9 @@
|
|||
<EmbeddedResource Include="Zoom\index.html" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.864.35\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.864.35\build\Microsoft.Web.WebView2.targets')" />
|
||||
|
|
|
@ -14,19 +14,9 @@
|
|||
<script src="https://source.zoom.us/zoom-meeting-1.9.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9/crypto-js.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
const API_KEY = '%%_API_KEY_%%';
|
||||
const API_SECRET = '%%_API_SECRET_%%';
|
||||
const ATTENDEE = 0;
|
||||
|
||||
var audioJoin = 0;
|
||||
var configuration = {
|
||||
leaveUrl: 'doesnotexist',
|
||||
meetingNumber: '%%_MEETING_NUMBER_%%',
|
||||
passWord: '%%_PASSWORD_%%',
|
||||
role: ATTENDEE,
|
||||
userName: '%%_USER_NAME_%%'
|
||||
};
|
||||
var signature = '%%_SIGNATURE_%%';
|
||||
var videoJoin = 0;
|
||||
|
||||
function initializeUserInterface(_) {
|
||||
|
@ -69,81 +59,94 @@
|
|||
}
|
||||
|
||||
requestAnimationFrame(initializeUserInterface);
|
||||
};
|
||||
|
||||
if (!ZoomMtg.checkSystemRequirements()) {
|
||||
alert('This system does not meet the necessary requirements for Zoom!');
|
||||
}
|
||||
|
||||
ZoomMtg.setZoomJSLib('https://source.zoom.us/1.9.1/lib', '/av');
|
||||
ZoomMtg.preLoadWasm();
|
||||
ZoomMtg.prepareJssdk();
|
||||
function startMeeting(credentials) {
|
||||
var signature = credentials.signature;
|
||||
|
||||
if (!signature) {
|
||||
signature = ZoomMtg.generateSignature({
|
||||
meetingNumber: configuration.meetingNumber,
|
||||
apiKey: API_KEY,
|
||||
apiSecret: API_SECRET,
|
||||
role: configuration.role,
|
||||
if (!ZoomMtg.checkSystemRequirements()) {
|
||||
alert('This system does not meet the necessary requirements for Zoom!');
|
||||
}
|
||||
|
||||
ZoomMtg.setZoomJSLib('https://source.zoom.us/1.9.1/lib', '/av');
|
||||
ZoomMtg.preLoadWasm();
|
||||
ZoomMtg.prepareJssdk();
|
||||
|
||||
if (!signature) {
|
||||
signature = ZoomMtg.generateSignature({
|
||||
meetingNumber: credentials.meetingNumber,
|
||||
apiKey: credentials.apiKey,
|
||||
apiSecret: credentials.apiSecret,
|
||||
role: ATTENDEE,
|
||||
error: function (res) {
|
||||
alert(`Failed to generate signature: ${JSON.stringify(res)}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ZoomMtg.init({
|
||||
audioPanelAlwaysOpen: false,
|
||||
disableCallOut: true,
|
||||
disableInvite: true,
|
||||
disableJoinAudio: false,
|
||||
disableRecord: true,
|
||||
disableReport: true,
|
||||
disableVoIP: false,
|
||||
leaveUrl: 'doesnotexist',
|
||||
isLockBottom: true,
|
||||
isShowJoiningErrorDialog: true,
|
||||
isSupportAV: true,
|
||||
isSupportBreakout: false,
|
||||
isSupportChat: '%_ALLOW_CHAT_%',
|
||||
isSupportCC: '%_ALLOW_CLOSED_CAPTIONS_%',
|
||||
isSupportPolling: false,
|
||||
isSupportQA: false,
|
||||
isSupportNonverbal: false,
|
||||
screenShare: false,
|
||||
sharingMode: 'both',
|
||||
showMeetingHeader: true,
|
||||
showPureSharingContent: false,
|
||||
videoDrag: true,
|
||||
videoHeader: true,
|
||||
meetingInfo: [
|
||||
'topic',
|
||||
'host',
|
||||
'participant',
|
||||
//'mn',
|
||||
//'pwd',
|
||||
//'telPwd',
|
||||
//'invite',
|
||||
//'dc'
|
||||
],
|
||||
error: function (res) {
|
||||
alert(`Failed to generate signature: ${JSON.stringify(res)}`);
|
||||
alert(`Failed to initialize meeting: ${JSON.stringify(res)}`);
|
||||
},
|
||||
success: function () {
|
||||
ZoomMtg.join({
|
||||
apiKey: credentials.apiKey,
|
||||
meetingNumber: credentials.meetingNumber,
|
||||
passWord: credentials.password,
|
||||
signature: signature,
|
||||
userName: credentials.userName,
|
||||
error: function (res) {
|
||||
alert(`Failed to join meeting: ${JSON.stringify(res)}`);
|
||||
},
|
||||
success: function (res) {
|
||||
requestAnimationFrame(initializeUserInterface);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ZoomMtg.init({
|
||||
audioPanelAlwaysOpen: false,
|
||||
disableCallOut: true,
|
||||
disableInvite: true,
|
||||
disableJoinAudio: false,
|
||||
disableRecord: true,
|
||||
disableReport: true,
|
||||
disableVoIP: false,
|
||||
leaveUrl: configuration.leaveUrl,
|
||||
isLockBottom: true,
|
||||
isShowJoiningErrorDialog: true,
|
||||
isSupportAV: true,
|
||||
isSupportBreakout: false,
|
||||
isSupportChat: '%_ALLOW_CHAT_%',
|
||||
isSupportCC: '%_ALLOW_CLOSED_CAPTIONS_%',
|
||||
isSupportPolling: false,
|
||||
isSupportQA: false,
|
||||
isSupportNonverbal: false,
|
||||
screenShare: false,
|
||||
sharingMode: 'both',
|
||||
showMeetingHeader: true,
|
||||
showPureSharingContent: false,
|
||||
videoDrag: true,
|
||||
videoHeader: true,
|
||||
meetingInfo: [
|
||||
'topic',
|
||||
'host',
|
||||
'participant',
|
||||
//'mn',
|
||||
//'pwd',
|
||||
//'telPwd',
|
||||
//'invite',
|
||||
//'dc'
|
||||
],
|
||||
error: function (res) {
|
||||
alert(`Failed to initialize meeting: ${JSON.stringify(res)}`);
|
||||
},
|
||||
success: function () {
|
||||
ZoomMtg.join({
|
||||
apiKey: API_KEY,
|
||||
meetingNumber: configuration.meetingNumber,
|
||||
passWord: configuration.passWord,
|
||||
signature: signature,
|
||||
userName: configuration.userName,
|
||||
error: function (res) {
|
||||
alert(`Failed to join meeting: ${JSON.stringify(res)}`);
|
||||
},
|
||||
success: function (res) {
|
||||
requestAnimationFrame(initializeUserInterface);
|
||||
}
|
||||
});
|
||||
function webMessageReceived(args) {
|
||||
if ('credentials' in args.data) {
|
||||
startMeeting(args.data.credentials);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.chrome.webview.addEventListener('message', webMessageReceived);
|
||||
window.chrome.webview.postMessage('credentials');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.864.35" targetFramework="net472" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
||||
</packages>
|
Loading…
Reference in a new issue