SEBWIN-301: Implemented basic service session procedure.
This commit is contained in:
parent
77a3b50ca9
commit
6b24554abc
61 changed files with 911 additions and 296 deletions
|
@ -105,7 +105,12 @@ namespace SafeExamBrowser.Client.UnitTests.Communication
|
||||||
sut.AuthenticationToken = token;
|
sut.AuthenticationToken = token;
|
||||||
|
|
||||||
var connectionResponse = sut.Connect(token);
|
var connectionResponse = sut.Connect(token);
|
||||||
var response = sut.Disconnect(new DisconnectionMessage { CommunicationToken = connectionResponse.CommunicationToken.Value });
|
var message = new DisconnectionMessage
|
||||||
|
{
|
||||||
|
CommunicationToken = connectionResponse.CommunicationToken.Value,
|
||||||
|
Interlocutor = Interlocutor.Runtime
|
||||||
|
};
|
||||||
|
var response = sut.Disconnect(message);
|
||||||
|
|
||||||
Assert.IsNotNull(response);
|
Assert.IsNotNull(response);
|
||||||
Assert.IsTrue(response.ConnectionTerminated);
|
Assert.IsTrue(response.ConnectionTerminated);
|
||||||
|
@ -118,8 +123,13 @@ namespace SafeExamBrowser.Client.UnitTests.Communication
|
||||||
{
|
{
|
||||||
var token = sut.AuthenticationToken = Guid.NewGuid();
|
var token = sut.AuthenticationToken = Guid.NewGuid();
|
||||||
var response = sut.Connect(token);
|
var response = sut.Connect(token);
|
||||||
|
var message = new DisconnectionMessage
|
||||||
|
{
|
||||||
|
CommunicationToken = response.CommunicationToken.Value,
|
||||||
|
Interlocutor = Interlocutor.Runtime
|
||||||
|
};
|
||||||
|
|
||||||
sut.Disconnect(new DisconnectionMessage { CommunicationToken = response.CommunicationToken.Value });
|
sut.Disconnect(message);
|
||||||
sut.AuthenticationToken = token = Guid.NewGuid();
|
sut.AuthenticationToken = token = Guid.NewGuid();
|
||||||
|
|
||||||
response = sut.Connect(token);
|
response = sut.Connect(token);
|
||||||
|
@ -274,7 +284,7 @@ namespace SafeExamBrowser.Client.UnitTests.Communication
|
||||||
sut.Send(new PasswordRequestMessage(default(PasswordRequestPurpose), Guid.Empty) { CommunicationToken = token });
|
sut.Send(new PasswordRequestMessage(default(PasswordRequestPurpose), Guid.Empty) { CommunicationToken = token });
|
||||||
sut.Send(new ReconfigurationDeniedMessage("") { CommunicationToken = token });
|
sut.Send(new ReconfigurationDeniedMessage("") { CommunicationToken = token });
|
||||||
sut.Send(new SimpleMessage(SimpleMessagePurport.Shutdown) { CommunicationToken = token });
|
sut.Send(new SimpleMessage(SimpleMessagePurport.Shutdown) { CommunicationToken = token });
|
||||||
sut.Disconnect(new DisconnectionMessage { CommunicationToken = token });
|
sut.Disconnect(new DisconnectionMessage { CommunicationToken = token, Interlocutor = Interlocutor.Runtime });
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessage : Message { };
|
private class TestMessage : Message { };
|
||||||
|
|
|
@ -65,12 +65,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||||
{
|
{
|
||||||
runtime.Setup(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>())).Returns(true);
|
runtime.Setup(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>())).Returns(true);
|
||||||
runtime.Setup(r => r.Disconnect()).Returns(true);
|
runtime.Setup(r => r.Disconnect()).Returns(true);
|
||||||
|
runtime.SetupGet(r => r.IsConnected).Returns(true);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
var result = sut.Revert();
|
var result = sut.Revert();
|
||||||
|
|
||||||
runtime.Verify(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>()), Times.Once);
|
runtime.Verify(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>()), Times.Once);
|
||||||
runtime.Verify(r => r.Disconnect(), Times.Once);
|
runtime.Verify(r => r.Disconnect(), Times.Once);
|
||||||
|
runtime.VerifyGet(r => r.IsConnected, Times.Once);
|
||||||
runtime.VerifyNoOtherCalls();
|
runtime.VerifyNoOtherCalls();
|
||||||
|
|
||||||
Assert.AreEqual(OperationResult.Success, result);
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
|
@ -81,12 +83,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||||
{
|
{
|
||||||
runtime.Setup(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>())).Returns(true);
|
runtime.Setup(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>())).Returns(true);
|
||||||
runtime.Setup(r => r.Disconnect()).Returns(false);
|
runtime.Setup(r => r.Disconnect()).Returns(false);
|
||||||
|
runtime.SetupGet(r => r.IsConnected).Returns(true);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
var result = sut.Revert();
|
var result = sut.Revert();
|
||||||
|
|
||||||
runtime.Verify(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>()), Times.Once);
|
runtime.Verify(r => r.Connect(It.IsAny<Guid>(), It.IsAny<bool>()), Times.Once);
|
||||||
runtime.Verify(r => r.Disconnect(), Times.Once);
|
runtime.Verify(r => r.Disconnect(), Times.Once);
|
||||||
|
runtime.VerifyGet(r => r.IsConnected, Times.Once);
|
||||||
runtime.VerifyNoOtherCalls();
|
runtime.VerifyNoOtherCalls();
|
||||||
|
|
||||||
Assert.AreEqual(OperationResult.Failed, result);
|
Assert.AreEqual(OperationResult.Failed, result);
|
||||||
|
@ -97,6 +101,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||||
{
|
{
|
||||||
var result = sut.Revert();
|
var result = sut.Revert();
|
||||||
|
|
||||||
|
runtime.VerifyGet(r => r.IsConnected, Times.Once);
|
||||||
runtime.VerifyNoOtherCalls();
|
runtime.VerifyNoOtherCalls();
|
||||||
|
|
||||||
Assert.AreEqual(OperationResult.Success, result);
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Events;
|
using SafeExamBrowser.Contracts.Communication.Events;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
|
@ -53,10 +54,13 @@ namespace SafeExamBrowser.Client.Communication
|
||||||
return accepted;
|
return accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisconnect()
|
protected override void OnDisconnect(Interlocutor interlocutor)
|
||||||
{
|
{
|
||||||
RuntimeDisconnected?.Invoke();
|
if (interlocutor == Interlocutor.Runtime)
|
||||||
IsConnected = false;
|
{
|
||||||
|
RuntimeDisconnected?.Invoke();
|
||||||
|
IsConnected = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Response OnReceive(Message message)
|
protected override Response OnReceive(Message message)
|
||||||
|
|
|
@ -20,6 +20,7 @@ using SafeExamBrowser.Communication.Proxies;
|
||||||
using SafeExamBrowser.Configuration.Cryptography;
|
using SafeExamBrowser.Configuration.Cryptography;
|
||||||
using SafeExamBrowser.Contracts.Browser;
|
using SafeExamBrowser.Contracts.Browser;
|
||||||
using SafeExamBrowser.Contracts.Client;
|
using SafeExamBrowser.Contracts.Client;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
@ -97,7 +98,7 @@ namespace SafeExamBrowser.Client
|
||||||
powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text);
|
powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text);
|
||||||
processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods);
|
processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods);
|
||||||
uiFactory = BuildUserInterfaceFactory();
|
uiFactory = BuildUserInterfaceFactory();
|
||||||
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)));
|
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)), Interlocutor.Client);
|
||||||
taskbar = BuildTaskbar();
|
taskbar = BuildTaskbar();
|
||||||
terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator)));
|
terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator)));
|
||||||
windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods);
|
windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods);
|
||||||
|
|
|
@ -17,7 +17,6 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
{
|
{
|
||||||
internal class RuntimeConnectionOperation : IOperation
|
internal class RuntimeConnectionOperation : IOperation
|
||||||
{
|
{
|
||||||
private bool connected;
|
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private IRuntimeProxy runtime;
|
private IRuntimeProxy runtime;
|
||||||
private Guid token;
|
private Guid token;
|
||||||
|
@ -37,7 +36,7 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
logger.Info("Initializing runtime connection...");
|
logger.Info("Initializing runtime connection...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeRuntimeConnection);
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeRuntimeConnection);
|
||||||
|
|
||||||
connected = runtime.Connect(token);
|
var connected = runtime.Connect(token);
|
||||||
|
|
||||||
if (connected)
|
if (connected)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +55,7 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
logger.Info("Closing runtime connection...");
|
logger.Info("Closing runtime connection...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_CloseRuntimeConnection);
|
StatusChanged?.Invoke(TextKey.OperationStatus_CloseRuntimeConnection);
|
||||||
|
|
||||||
if (connected)
|
if (runtime.IsConnected)
|
||||||
{
|
{
|
||||||
var success = runtime.Disconnect();
|
var success = runtime.Disconnect();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -17,7 +19,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||||
internal class BaseHostStub : BaseHost
|
internal class BaseHostStub : BaseHost
|
||||||
{
|
{
|
||||||
public Func<Guid?, bool> OnConnectStub { get; set; }
|
public Func<Guid?, bool> OnConnectStub { get; set; }
|
||||||
public Action OnDisconnectStub { get; set; }
|
public Action<Interlocutor> OnDisconnectStub { get; set; }
|
||||||
public Func<Message, Response> OnReceiveStub { get; set; }
|
public Func<Message, Response> OnReceiveStub { get; set; }
|
||||||
public Func<SimpleMessagePurport, Response> OnReceiveSimpleMessageStub { get; set; }
|
public Func<SimpleMessagePurport, Response> OnReceiveSimpleMessageStub { get; set; }
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||||
|
|
||||||
public Guid? GetCommunicationToken()
|
public Guid? GetCommunicationToken()
|
||||||
{
|
{
|
||||||
return CommunicationToken;
|
return CommunicationToken.Any() ? CommunicationToken.First() : default(Guid?);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnConnect(Guid? token)
|
protected override bool OnConnect(Guid? token)
|
||||||
|
@ -35,9 +37,9 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||||
return OnConnectStub?.Invoke(token) == true;
|
return OnConnectStub?.Invoke(token) == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisconnect()
|
protected override void OnDisconnect(Interlocutor interlocutor)
|
||||||
{
|
{
|
||||||
OnDisconnectStub?.Invoke();
|
OnDisconnectStub?.Invoke(interlocutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Response OnReceive(Message message)
|
protected override Response OnReceive(Message message)
|
||||||
|
|
|
@ -154,17 +154,19 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustCorrectlyHandleDisconnectionRequest()
|
public void MustCorrectlyHandleDisconnectionRequest()
|
||||||
{
|
{
|
||||||
var message = new DisconnectionMessage();
|
var message = new DisconnectionMessage { Interlocutor = Interlocutor.Runtime };
|
||||||
var disconnected = false;
|
var disconnected = false;
|
||||||
|
var interlocutor = Interlocutor.Unknown;
|
||||||
|
|
||||||
sut.OnConnectStub = (t) => { return true; };
|
sut.OnConnectStub = (t) => { return true; };
|
||||||
sut.OnDisconnectStub = () => disconnected = true;
|
sut.OnDisconnectStub = (i) => { disconnected = true; interlocutor = i; };
|
||||||
sut.Connect();
|
sut.Connect();
|
||||||
|
|
||||||
message.CommunicationToken = sut.GetCommunicationToken().Value;
|
message.CommunicationToken = sut.GetCommunicationToken().Value;
|
||||||
|
|
||||||
var response = sut.Disconnect(message);
|
var response = sut.Disconnect(message);
|
||||||
|
|
||||||
|
Assert.AreEqual(message.Interlocutor, interlocutor);
|
||||||
Assert.IsTrue(disconnected);
|
Assert.IsTrue(disconnected);
|
||||||
Assert.IsTrue(response.ConnectionTerminated);
|
Assert.IsTrue(response.ConnectionTerminated);
|
||||||
Assert.IsNull(sut.GetCommunicationToken());
|
Assert.IsNull(sut.GetCommunicationToken());
|
||||||
|
@ -176,7 +178,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||||
var disconnected = false;
|
var disconnected = false;
|
||||||
|
|
||||||
sut.OnConnectStub = (t) => { return true; };
|
sut.OnConnectStub = (t) => { return true; };
|
||||||
sut.OnDisconnectStub = () => disconnected = true;
|
sut.OnDisconnectStub = (i) => disconnected = true;
|
||||||
sut.Connect();
|
sut.Connect();
|
||||||
|
|
||||||
var response = sut.Disconnect(new DisconnectionMessage());
|
var response = sut.Disconnect(new DisconnectionMessage());
|
||||||
|
|
|
@ -7,16 +7,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||||
{
|
{
|
||||||
internal class BaseProxyImpl : BaseProxy
|
internal class BaseProxyImpl : BaseProxy
|
||||||
{
|
{
|
||||||
public BaseProxyImpl(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
|
public BaseProxyImpl(string address, IProxyObjectFactory factory, ILogger logger, Interlocutor owner) : base(address, factory, logger, owner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System;
|
||||||
using System.ServiceModel;
|
using System.ServiceModel;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -29,7 +30,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||||
proxyObjectFactory = new Mock<IProxyObjectFactory>();
|
proxyObjectFactory = new Mock<IProxyObjectFactory>();
|
||||||
logger = new Mock<ILogger>();
|
logger = new Mock<ILogger>();
|
||||||
|
|
||||||
sut = new BaseProxyImpl("net.pipe://some/address/here", proxyObjectFactory.Object, logger.Object);
|
sut = new BaseProxyImpl("net.pipe://some/address/here", proxyObjectFactory.Object, logger.Object, default(Interlocutor));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
|
|
@ -11,6 +11,7 @@ using System.ServiceModel;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -43,7 +44,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||||
|
|
||||||
sut = new ClientProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
|
sut = new ClientProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object, default(Interlocutor));
|
||||||
sut.Connect(Guid.NewGuid());
|
sut.Connect(Guid.NewGuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ using System.ServiceModel;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
@ -44,7 +45,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||||
|
|
||||||
sut = new RuntimeProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
|
sut = new RuntimeProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object, default(Interlocutor));
|
||||||
sut.Connect(Guid.NewGuid());
|
sut.Connect(Guid.NewGuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||||
{
|
{
|
||||||
|
@ -42,7 +43,7 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||||
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
proxy.Setup(o => o.State).Returns(CommunicationState.Opened);
|
||||||
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
proxyObjectFactory.Setup(f => f.CreateObject(It.IsAny<string>())).Returns(proxy.Object);
|
||||||
|
|
||||||
sut = new ServiceProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object);
|
sut = new ServiceProxy("net.pipe://random/address/here", proxyObjectFactory.Object, logger.Object, default(Interlocutor));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ServiceModel;
|
using System.ServiceModel;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using SafeExamBrowser.Contracts.Communication;
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
|
@ -30,7 +31,7 @@ namespace SafeExamBrowser.Communication.Hosts
|
||||||
private Thread hostThread;
|
private Thread hostThread;
|
||||||
private int timeout_ms;
|
private int timeout_ms;
|
||||||
|
|
||||||
protected Guid? CommunicationToken { get; private set; }
|
protected IList<Guid> CommunicationToken { get; private set; }
|
||||||
protected ILogger Logger { get; private set; }
|
protected ILogger Logger { get; private set; }
|
||||||
|
|
||||||
public bool IsRunning
|
public bool IsRunning
|
||||||
|
@ -47,13 +48,14 @@ namespace SafeExamBrowser.Communication.Hosts
|
||||||
public BaseHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms)
|
public BaseHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms)
|
||||||
{
|
{
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
this.CommunicationToken = new List<Guid>();
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.Logger = logger;
|
this.Logger = logger;
|
||||||
this.timeout_ms = timeout_ms;
|
this.timeout_ms = timeout_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract bool OnConnect(Guid? token);
|
protected abstract bool OnConnect(Guid? token);
|
||||||
protected abstract void OnDisconnect();
|
protected abstract void OnDisconnect(Interlocutor interlocutor);
|
||||||
protected abstract Response OnReceive(Message message);
|
protected abstract Response OnReceive(Message message);
|
||||||
protected abstract Response OnReceive(SimpleMessagePurport message);
|
protected abstract Response OnReceive(SimpleMessagePurport message);
|
||||||
|
|
||||||
|
@ -61,15 +63,19 @@ namespace SafeExamBrowser.Communication.Hosts
|
||||||
{
|
{
|
||||||
lock (@lock)
|
lock (@lock)
|
||||||
{
|
{
|
||||||
Logger.Debug($"Received connection request with authentication token '{token}'.");
|
Logger.Debug($"Received connection request {(token.HasValue ? $"with authentication token '{token}'" : "without authentication token")}.");
|
||||||
|
|
||||||
var response = new ConnectionResponse();
|
var response = new ConnectionResponse();
|
||||||
var connected = OnConnect(token);
|
var connected = OnConnect(token);
|
||||||
|
|
||||||
if (connected)
|
if (connected)
|
||||||
{
|
{
|
||||||
response.CommunicationToken = CommunicationToken = Guid.NewGuid();
|
var communicationToken = Guid.NewGuid();
|
||||||
|
|
||||||
|
response.CommunicationToken = communicationToken;
|
||||||
response.ConnectionEstablished = true;
|
response.ConnectionEstablished = true;
|
||||||
|
|
||||||
|
CommunicationToken.Add(communicationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request.");
|
Logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request.");
|
||||||
|
@ -88,10 +94,10 @@ namespace SafeExamBrowser.Communication.Hosts
|
||||||
|
|
||||||
if (IsAuthorized(message?.CommunicationToken))
|
if (IsAuthorized(message?.CommunicationToken))
|
||||||
{
|
{
|
||||||
OnDisconnect();
|
OnDisconnect(message.Interlocutor);
|
||||||
|
|
||||||
CommunicationToken = null;
|
|
||||||
response.ConnectionTerminated = true;
|
response.ConnectionTerminated = true;
|
||||||
|
CommunicationToken.Remove(message.CommunicationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -166,7 +172,7 @@ namespace SafeExamBrowser.Communication.Hosts
|
||||||
|
|
||||||
private bool IsAuthorized(Guid? token)
|
private bool IsAuthorized(Guid? token)
|
||||||
{
|
{
|
||||||
return CommunicationToken == token;
|
return token.HasValue && CommunicationToken.Contains(token.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryStartHost(AutoResetEvent startedEvent, out Exception exception)
|
private void TryStartHost(AutoResetEvent startedEvent, out Exception exception)
|
||||||
|
|
|
@ -26,21 +26,23 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
private readonly object @lock = new object();
|
private readonly object @lock = new object();
|
||||||
|
|
||||||
private string address;
|
private string address;
|
||||||
|
private Interlocutor owner;
|
||||||
private IProxyObject proxy;
|
private IProxyObject proxy;
|
||||||
private IProxyObjectFactory factory;
|
private IProxyObjectFactory factory;
|
||||||
private Guid? communicationToken;
|
private Guid? communicationToken;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
protected bool IsConnected { get { return communicationToken.HasValue; } }
|
protected ILogger Logger { get; }
|
||||||
protected ILogger Logger { get; private set; }
|
public bool IsConnected => communicationToken.HasValue;
|
||||||
|
|
||||||
public event CommunicationEventHandler ConnectionLost;
|
public event CommunicationEventHandler ConnectionLost;
|
||||||
|
|
||||||
public BaseProxy(string address, IProxyObjectFactory factory, ILogger logger)
|
public BaseProxy(string address, IProxyObjectFactory factory, ILogger logger, Interlocutor owner)
|
||||||
{
|
{
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.Logger = logger;
|
this.Logger = logger;
|
||||||
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool Connect(Guid? token = null, bool autoPing = true)
|
public virtual bool Connect(Guid? token = null, bool autoPing = true)
|
||||||
|
@ -89,7 +91,11 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
|
|
||||||
StopAutoPing();
|
StopAutoPing();
|
||||||
|
|
||||||
var message = new DisconnectionMessage { CommunicationToken = communicationToken.Value };
|
var message = new DisconnectionMessage
|
||||||
|
{
|
||||||
|
CommunicationToken = communicationToken.Value,
|
||||||
|
Interlocutor = owner
|
||||||
|
};
|
||||||
var response = proxy.Disconnect(message);
|
var response = proxy.Disconnect(message);
|
||||||
var success = response.ConnectionTerminated;
|
var success = response.ConnectionTerminated;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -19,7 +20,7 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ClientProxy : BaseProxy, IClientProxy
|
public class ClientProxy : BaseProxy, IClientProxy
|
||||||
{
|
{
|
||||||
public ClientProxy(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
|
public ClientProxy(string address, IProxyObjectFactory factory, ILogger logger, Interlocutor owner) : base(address, factory, logger, owner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
SafeExamBrowser.Communication/Proxies/ProxyFactory.cs
Normal file
34
SafeExamBrowser.Communication/Proxies/ProxyFactory.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Communication.Proxies
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation of the <see cref="IProxyFactory"/>, creating instances of the default proxy implementations.
|
||||||
|
/// </summary>
|
||||||
|
public class ProxyFactory : IProxyFactory
|
||||||
|
{
|
||||||
|
private IProxyObjectFactory factory;
|
||||||
|
private IModuleLogger logger;
|
||||||
|
|
||||||
|
public ProxyFactory(IProxyObjectFactory factory, IModuleLogger logger)
|
||||||
|
{
|
||||||
|
this.factory = factory;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IClientProxy CreateClientProxy(string address, Interlocutor owner)
|
||||||
|
{
|
||||||
|
return new ClientProxy(address, factory, logger.CloneFor(nameof(ClientProxy)), owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -19,7 +20,7 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RuntimeProxy : BaseProxy, IRuntimeProxy
|
public class RuntimeProxy : BaseProxy, IRuntimeProxy
|
||||||
{
|
{
|
||||||
public RuntimeProxy(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
|
public RuntimeProxy(string address, IProxyObjectFactory factory, ILogger logger, Interlocutor owner) : base(address, factory, logger, owner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -19,9 +21,8 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
public class ServiceProxy : BaseProxy, IServiceProxy
|
public class ServiceProxy : BaseProxy, IServiceProxy
|
||||||
{
|
{
|
||||||
public bool Ignore { private get; set; }
|
public bool Ignore { private get; set; }
|
||||||
public new bool IsConnected => base.IsConnected;
|
|
||||||
|
|
||||||
public ServiceProxy(string address, IProxyObjectFactory factory, ILogger logger) : base(address, factory, logger)
|
public ServiceProxy(string address, IProxyObjectFactory factory, ILogger logger, Interlocutor owner) : base(address, factory, logger, owner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,10 +53,28 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
return new CommunicationResult(true);
|
return new CommunicationResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement service communication...
|
try
|
||||||
// Send(new StartSessionMessage { Id = sessionId, Settings = settings });
|
{
|
||||||
|
var response = Send(new SessionStartMessage(configuration));
|
||||||
|
var success = IsAcknowledged(response);
|
||||||
|
|
||||||
throw new NotImplementedException();
|
if (success)
|
||||||
|
{
|
||||||
|
Logger.Debug("Service acknowledged session start.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error($"Service did not acknowledge session start! Received: {ToString(response)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommunicationResult(success);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error($"Failed to perform '{nameof(StartSession)}'", e);
|
||||||
|
|
||||||
|
return new CommunicationResult(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommunicationResult StopSession(Guid sessionId)
|
public CommunicationResult StopSession(Guid sessionId)
|
||||||
|
@ -65,10 +84,28 @@ namespace SafeExamBrowser.Communication.Proxies
|
||||||
return new CommunicationResult(true);
|
return new CommunicationResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement service communication...
|
try
|
||||||
// Send(new StopSessionMessage { SessionId = sessionId });
|
{
|
||||||
|
var response = Send(new SessionStopMessage(sessionId));
|
||||||
|
var success = IsAcknowledged(response);
|
||||||
|
|
||||||
throw new NotImplementedException();
|
if (success)
|
||||||
|
{
|
||||||
|
Logger.Debug("Service acknowledged session stop.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error($"Service did not acknowledge session stop! Received: {ToString(response)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CommunicationResult(success);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error($"Failed to perform '{nameof(StopSession)}'", e);
|
||||||
|
|
||||||
|
return new CommunicationResult(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IgnoreOperation(string operationName)
|
private bool IgnoreOperation(string operationName)
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Proxies\BaseProxy.cs" />
|
<Compile Include="Proxies\BaseProxy.cs" />
|
||||||
<Compile Include="Proxies\ClientProxy.cs" />
|
<Compile Include="Proxies\ClientProxy.cs" />
|
||||||
|
<Compile Include="Proxies\ProxyFactory.cs" />
|
||||||
<Compile Include="Proxies\ProxyObjectFactory.cs" />
|
<Compile Include="Proxies\ProxyObjectFactory.cs" />
|
||||||
<Compile Include="Proxies\RuntimeProxy.cs" />
|
<Compile Include="Proxies\RuntimeProxy.cs" />
|
||||||
<Compile Include="Proxies\ServiceProxy.cs" />
|
<Compile Include="Proxies\ServiceProxy.cs" />
|
||||||
|
|
|
@ -278,7 +278,6 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
Assert.IsNull(configuration.Settings);
|
Assert.IsNull(configuration.Settings);
|
||||||
Assert.IsInstanceOfType(configuration.AppConfig, typeof(AppConfig));
|
Assert.IsInstanceOfType(configuration.AppConfig, typeof(AppConfig));
|
||||||
Assert.IsInstanceOfType(configuration.ClientAuthenticationToken, typeof(Guid));
|
Assert.IsInstanceOfType(configuration.ClientAuthenticationToken, typeof(Guid));
|
||||||
Assert.IsInstanceOfType(configuration.ServiceAuthenticationToken, typeof(Guid));
|
|
||||||
Assert.IsInstanceOfType(configuration.SessionId, typeof(Guid));
|
Assert.IsInstanceOfType(configuration.SessionId, typeof(Guid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,14 +291,18 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
var runtimeAddress = appConfig.RuntimeAddress;
|
var runtimeAddress = appConfig.RuntimeAddress;
|
||||||
var runtimeId = appConfig.RuntimeId;
|
var runtimeId = appConfig.RuntimeId;
|
||||||
var runtimeLogFilePath = appConfig.RuntimeLogFilePath;
|
var runtimeLogFilePath = appConfig.RuntimeLogFilePath;
|
||||||
|
var serviceEventName = appConfig.ServiceEventName;
|
||||||
|
|
||||||
var configuration = sut.InitializeSessionConfiguration();
|
var configuration = sut.InitializeSessionConfiguration();
|
||||||
|
|
||||||
Assert.AreNotEqual(configuration.AppConfig.ClientAddress, clientAddress);
|
|
||||||
Assert.AreNotEqual(configuration.AppConfig.ClientId, clientId);
|
|
||||||
Assert.AreEqual(configuration.AppConfig.ClientLogFilePath, clientLogFilePath);
|
Assert.AreEqual(configuration.AppConfig.ClientLogFilePath, clientLogFilePath);
|
||||||
Assert.AreEqual(configuration.AppConfig.RuntimeAddress, runtimeAddress);
|
Assert.AreEqual(configuration.AppConfig.RuntimeAddress, runtimeAddress);
|
||||||
Assert.AreEqual(configuration.AppConfig.RuntimeId, runtimeId);
|
Assert.AreEqual(configuration.AppConfig.RuntimeId, runtimeId);
|
||||||
Assert.AreEqual(configuration.AppConfig.RuntimeLogFilePath, runtimeLogFilePath);
|
Assert.AreEqual(configuration.AppConfig.RuntimeLogFilePath, runtimeLogFilePath);
|
||||||
|
|
||||||
|
Assert.AreNotEqual(configuration.AppConfig.ClientAddress, clientAddress);
|
||||||
|
Assert.AreNotEqual(configuration.AppConfig.ClientId, clientId);
|
||||||
|
Assert.AreNotEqual(configuration.AppConfig.ServiceEventName, serviceEventName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
@ -312,10 +315,8 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
|
|
||||||
Assert.AreNotEqual(firstSession.SessionId, secondSession.SessionId);
|
Assert.AreNotEqual(firstSession.SessionId, secondSession.SessionId);
|
||||||
Assert.AreNotEqual(firstSession.ClientAuthenticationToken, secondSession.ClientAuthenticationToken);
|
Assert.AreNotEqual(firstSession.ClientAuthenticationToken, secondSession.ClientAuthenticationToken);
|
||||||
Assert.AreNotEqual(firstSession.ServiceAuthenticationToken, secondSession.ServiceAuthenticationToken);
|
|
||||||
Assert.AreNotEqual(secondSession.SessionId, thirdSession.SessionId);
|
Assert.AreNotEqual(secondSession.SessionId, thirdSession.SessionId);
|
||||||
Assert.AreNotEqual(secondSession.ClientAuthenticationToken, thirdSession.ClientAuthenticationToken);
|
Assert.AreNotEqual(secondSession.ClientAuthenticationToken, thirdSession.ClientAuthenticationToken);
|
||||||
Assert.AreNotEqual(secondSession.ServiceAuthenticationToken, thirdSession.ServiceAuthenticationToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterModules()
|
private void RegisterModules()
|
||||||
|
|
|
@ -78,10 +78,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
|
|
||||||
appConfig.ClientId = Guid.NewGuid();
|
appConfig.ClientId = Guid.NewGuid();
|
||||||
appConfig.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}";
|
appConfig.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}";
|
||||||
|
appConfig.ServiceEventName = $@"Global\{nameof(SafeExamBrowser)}-{Guid.NewGuid()}";
|
||||||
|
|
||||||
configuration.AppConfig = appConfig.Clone();
|
configuration.AppConfig = appConfig.Clone();
|
||||||
configuration.ClientAuthenticationToken = Guid.NewGuid();
|
configuration.ClientAuthenticationToken = Guid.NewGuid();
|
||||||
configuration.ServiceAuthenticationToken = Guid.NewGuid();
|
|
||||||
configuration.SessionId = Guid.NewGuid();
|
configuration.SessionId = Guid.NewGuid();
|
||||||
|
|
||||||
return configuration;
|
return configuration;
|
||||||
|
|
|
@ -16,5 +16,9 @@ namespace SafeExamBrowser.Contracts.Communication.Data
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class DisconnectionMessage : Message
|
public class DisconnectionMessage : Message
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the component sending the message.
|
||||||
|
/// </summary>
|
||||||
|
public Interlocutor Interlocutor { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Contracts.Communication.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This message is transmitted to the service to request the initialization of a new session.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class SessionStartMessage : Message
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The configuration to be used by the service.
|
||||||
|
/// </summary>
|
||||||
|
public ServiceConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
public SessionStartMessage(ServiceConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Contracts.Communication.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This message is transmitted to the service to request the termination of a currently running session.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class SessionStopMessage : Message
|
||||||
|
{
|
||||||
|
public Guid SessionId { get; }
|
||||||
|
|
||||||
|
public SessionStopMessage(Guid sessionId)
|
||||||
|
{
|
||||||
|
SessionId = sessionId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Contracts.Communication.Events
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The event arguments used for the session start event fired by the <see cref="Hosts.IServiceHost"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class SessionStartEventArgs : CommunicationEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The configuration to be used for the new session.
|
||||||
|
/// </summary>
|
||||||
|
public ServiceConfiguration Configuration { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Contracts.Communication.Events
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The event arguments used for the session stop event fired by the <see cref="Hosts.IServiceHost"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class SessionStopEventArgs : CommunicationEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The identifier of the session to be stopped.
|
||||||
|
/// </summary>
|
||||||
|
public Guid SessionId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The token used for initial authentication.
|
/// The token used for initial authentication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Guid AuthenticationToken { set; }
|
Guid? AuthenticationToken { set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when the client disconnected from the runtime.
|
/// Event fired when the client disconnected from the runtime.
|
||||||
|
@ -56,26 +56,6 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
|
event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fired when the service disconnected from the runtime.
|
|
||||||
/// </summary>
|
|
||||||
event CommunicationEventHandler ServiceDisconnected;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fired when the service has experienced a critical failure.
|
|
||||||
/// </summary>
|
|
||||||
event CommunicationEventHandler ServiceFailed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fired when the service has successfully started a new session.
|
|
||||||
/// </summary>
|
|
||||||
event CommunicationEventHandler ServiceSessionStarted;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fired when the service has successfully stopped the currently running session.
|
|
||||||
/// </summary>
|
|
||||||
event CommunicationEventHandler ServiceSessionStopped;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when the client requests to shut down the application.
|
/// Event fired when the client requests to shut down the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Communication.Events;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Contracts.Communication.Hosts
|
namespace SafeExamBrowser.Contracts.Communication.Hosts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -13,5 +15,19 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IServiceHost : ICommunicationHost
|
public interface IServiceHost : ICommunicationHost
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether another application component may establish a connection with the host.
|
||||||
|
/// </summary>
|
||||||
|
bool AllowConnection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when the runtime requested to start a new session.
|
||||||
|
/// </summary>
|
||||||
|
event CommunicationEventHandler<SessionStartEventArgs> SessionStartRequested;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when the runtime requested to stop a running session.
|
||||||
|
/// </summary>
|
||||||
|
event CommunicationEventHandler<SessionStopEventArgs> SessionStopRequested;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ namespace SafeExamBrowser.Contracts.Communication
|
||||||
[ServiceKnownType(typeof(ReconfigurationMessage))]
|
[ServiceKnownType(typeof(ReconfigurationMessage))]
|
||||||
[ServiceKnownType(typeof(ReconfigurationDeniedMessage))]
|
[ServiceKnownType(typeof(ReconfigurationDeniedMessage))]
|
||||||
[ServiceKnownType(typeof(ServiceConfiguration))]
|
[ServiceKnownType(typeof(ServiceConfiguration))]
|
||||||
|
[ServiceKnownType(typeof(SessionStartMessage))]
|
||||||
|
[ServiceKnownType(typeof(SessionStopMessage))]
|
||||||
[ServiceKnownType(typeof(SimpleMessage))]
|
[ServiceKnownType(typeof(SimpleMessage))]
|
||||||
[ServiceKnownType(typeof(SimpleResponse))]
|
[ServiceKnownType(typeof(SimpleResponse))]
|
||||||
public interface ICommunication
|
public interface ICommunication
|
||||||
|
|
|
@ -17,6 +17,11 @@ namespace SafeExamBrowser.Contracts.Communication
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ICommunicationProxy
|
public interface ICommunicationProxy
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether a connection to the host has been established.
|
||||||
|
/// </summary>
|
||||||
|
bool IsConnected { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the connection to the host was lost, e.g. if a ping request failed or a communication fault occurred.
|
/// Fired when the connection to the host was lost, e.g. if a ping request failed or a communication fault occurred.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
39
SafeExamBrowser.Contracts/Communication/Interlocutor.cs
Normal file
39
SafeExamBrowser.Contracts/Communication/Interlocutor.cs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Contracts.Communication
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines all possible interlocutors for inter-process communication within the application.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public enum Interlocutor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The interlocutor is not an application component.
|
||||||
|
/// </summary>
|
||||||
|
Unknown = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The client application component.
|
||||||
|
/// </summary>
|
||||||
|
Client,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The runtime application component.
|
||||||
|
/// </summary>
|
||||||
|
Runtime,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The service application component.
|
||||||
|
/// </summary>
|
||||||
|
Service
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,8 @@ namespace SafeExamBrowser.Contracts.Communication.Proxies
|
||||||
public interface IProxyFactory
|
public interface IProxyFactory
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="IClientProxy"/> for the given endpoint address.
|
/// Creates a new <see cref="IClientProxy"/> for the given endpoint address and owner.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IClientProxy CreateClientProxy(string address);
|
IClientProxy CreateClientProxy(string address, Interlocutor owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,6 @@ namespace SafeExamBrowser.Contracts.Communication.Proxies
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Ignore { set; }
|
bool Ignore { set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether a connection to the communication host of the service has been established.
|
|
||||||
/// </summary>
|
|
||||||
bool IsConnected { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instructs the service to start a new session according to the given configuration.
|
/// Instructs the service to start a new session according to the given configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -116,6 +116,11 @@ namespace SafeExamBrowser.Contracts.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ServiceAddress { get; set; }
|
public string ServiceAddress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the global inter-process synchronization event hosted by the service.
|
||||||
|
/// </summary>
|
||||||
|
public string ServiceEventName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a shallow clone.
|
/// Creates a shallow clone.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -21,11 +21,6 @@ namespace SafeExamBrowser.Contracts.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AppConfig AppConfig { get; set; }
|
public AppConfig AppConfig { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The token used for initial authentication with the runtime.
|
|
||||||
/// </summary>
|
|
||||||
public Guid AuthenticationToken { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The unique identifier for the current session.
|
/// The unique identifier for the current session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -25,11 +25,6 @@ namespace SafeExamBrowser.Contracts.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid ClientAuthenticationToken { get; set; }
|
public Guid ClientAuthenticationToken { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The token used for initial communication authentication with the service.
|
|
||||||
/// </summary>
|
|
||||||
public Guid ServiceAuthenticationToken { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The unique session identifier.
|
/// The unique session identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -56,10 +56,15 @@
|
||||||
<Compile Include="Browser\ProgressChangedEventHandler.cs" />
|
<Compile Include="Browser\ProgressChangedEventHandler.cs" />
|
||||||
<Compile Include="Communication\Data\MessageBoxReplyMessage.cs" />
|
<Compile Include="Communication\Data\MessageBoxReplyMessage.cs" />
|
||||||
<Compile Include="Communication\Data\MessageBoxRequestMessage.cs" />
|
<Compile Include="Communication\Data\MessageBoxRequestMessage.cs" />
|
||||||
|
<Compile Include="Communication\Data\SessionStartMessage.cs" />
|
||||||
|
<Compile Include="Communication\Data\SessionStopMessage.cs" />
|
||||||
<Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" />
|
<Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" />
|
||||||
<Compile Include="Communication\Events\MessageBoxReplyEventArgs.cs" />
|
<Compile Include="Communication\Events\MessageBoxReplyEventArgs.cs" />
|
||||||
<Compile Include="Communication\Events\MessageBoxRequestEventArgs.cs" />
|
<Compile Include="Communication\Events\MessageBoxRequestEventArgs.cs" />
|
||||||
|
<Compile Include="Communication\Events\SessionStartEventArgs.cs" />
|
||||||
|
<Compile Include="Communication\Events\SessionStopEventArgs.cs" />
|
||||||
<Compile Include="Communication\Hosts\IServiceHost.cs" />
|
<Compile Include="Communication\Hosts\IServiceHost.cs" />
|
||||||
|
<Compile Include="Communication\Interlocutor.cs" />
|
||||||
<Compile Include="Configuration\ClientConfiguration.cs" />
|
<Compile Include="Configuration\ClientConfiguration.cs" />
|
||||||
<Compile Include="Configuration\Cryptography\EncryptionParameters.cs" />
|
<Compile Include="Configuration\Cryptography\EncryptionParameters.cs" />
|
||||||
<Compile Include="Configuration\Cryptography\ICertificateStore.cs" />
|
<Compile Include="Configuration\Cryptography\ICertificateStore.cs" />
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustOnlyAllowConnectionIfTokenIsValid()
|
public void MustAllowConnectionIfTokenIsValid()
|
||||||
{
|
{
|
||||||
var token = Guid.NewGuid();
|
var token = Guid.NewGuid();
|
||||||
|
|
||||||
|
@ -70,6 +70,19 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication
|
||||||
Assert.IsFalse(response.ConnectionEstablished);
|
Assert.IsFalse(response.ConnectionEstablished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void MustRejectConnectionIfNoAuthenticationTokenSet()
|
||||||
|
{
|
||||||
|
var token = Guid.Empty;
|
||||||
|
|
||||||
|
sut.AllowConnection = true;
|
||||||
|
|
||||||
|
var response = sut.Connect(token);
|
||||||
|
|
||||||
|
Assert.IsNotNull(response);
|
||||||
|
Assert.IsFalse(response.ConnectionEstablished);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustOnlyAllowOneConcurrentConnection()
|
public void MustOnlyAllowOneConcurrentConnection()
|
||||||
{
|
{
|
||||||
|
@ -94,7 +107,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustCorrectlyDisconnect()
|
public void MustCorrectlyDisconnectClient()
|
||||||
{
|
{
|
||||||
var disconnected = false;
|
var disconnected = false;
|
||||||
var token = Guid.NewGuid();
|
var token = Guid.NewGuid();
|
||||||
|
@ -104,7 +117,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication
|
||||||
sut.ClientDisconnected += () => disconnected = true;
|
sut.ClientDisconnected += () => disconnected = true;
|
||||||
|
|
||||||
var connectionResponse = sut.Connect(token);
|
var connectionResponse = sut.Connect(token);
|
||||||
var response = sut.Disconnect(new DisconnectionMessage { CommunicationToken = connectionResponse.CommunicationToken.Value });
|
var message = new DisconnectionMessage
|
||||||
|
{
|
||||||
|
CommunicationToken = connectionResponse.CommunicationToken.Value,
|
||||||
|
Interlocutor = Interlocutor.Client
|
||||||
|
};
|
||||||
|
var response = sut.Disconnect(message);
|
||||||
|
|
||||||
Assert.IsNotNull(response);
|
Assert.IsNotNull(response);
|
||||||
Assert.IsTrue(disconnected);
|
Assert.IsTrue(disconnected);
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
|
@ -63,7 +64,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
session.Settings = settings;
|
session.Settings = settings;
|
||||||
sessionContext.Current = session;
|
sessionContext.Current = session;
|
||||||
sessionContext.Next = session;
|
sessionContext.Next = session;
|
||||||
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>())).Returns(proxy.Object);
|
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>(), It.IsAny<Interlocutor>())).Returns(proxy.Object);
|
||||||
|
|
||||||
sut = new ClientOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, 0);
|
sut = new ClientOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
@ -60,7 +61,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
sessionContext.ClientProxy = proxy.Object;
|
sessionContext.ClientProxy = proxy.Object;
|
||||||
sessionContext.Current = session;
|
sessionContext.Current = session;
|
||||||
sessionContext.Next = session;
|
sessionContext.Next = session;
|
||||||
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>())).Returns(proxy.Object);
|
proxyFactory.Setup(f => f.CreateClientProxy(It.IsAny<string>(), It.IsAny<Interlocutor>())).Returns(proxy.Object);
|
||||||
|
|
||||||
sut = new ClientTerminationOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, 0);
|
sut = new ClientTerminationOperation(logger.Object, processFactory.Object, proxyFactory.Object, runtimeHost.Object, sessionContext, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
|
@ -24,9 +25,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class ServiceOperationTests
|
public class ServiceOperationTests
|
||||||
{
|
{
|
||||||
|
private AppConfig appConfig;
|
||||||
private Mock<ILogger> logger;
|
private Mock<ILogger> logger;
|
||||||
private Mock<IRuntimeHost> runtimeHost;
|
private Mock<IRuntimeHost> runtimeHost;
|
||||||
private Mock<IServiceProxy> service;
|
private Mock<IServiceProxy> service;
|
||||||
|
private EventWaitHandle serviceEvent;
|
||||||
private SessionConfiguration session;
|
private SessionConfiguration session;
|
||||||
private SessionContext sessionContext;
|
private SessionContext sessionContext;
|
||||||
private Settings settings;
|
private Settings settings;
|
||||||
|
@ -35,15 +38,22 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
var serviceEventName = $"{nameof(SafeExamBrowser)}-{nameof(ServiceOperationTests)}";
|
||||||
|
|
||||||
|
appConfig = new AppConfig();
|
||||||
logger = new Mock<ILogger>();
|
logger = new Mock<ILogger>();
|
||||||
runtimeHost = new Mock<IRuntimeHost>();
|
runtimeHost = new Mock<IRuntimeHost>();
|
||||||
service = new Mock<IServiceProxy>();
|
service = new Mock<IServiceProxy>();
|
||||||
|
serviceEvent = new EventWaitHandle(false, EventResetMode.AutoReset, serviceEventName);
|
||||||
session = new SessionConfiguration();
|
session = new SessionConfiguration();
|
||||||
sessionContext = new SessionContext();
|
sessionContext = new SessionContext();
|
||||||
settings = new Settings();
|
settings = new Settings();
|
||||||
|
|
||||||
|
appConfig.ServiceEventName = serviceEventName;
|
||||||
sessionContext.Current = session;
|
sessionContext.Current = session;
|
||||||
|
sessionContext.Current.AppConfig = appConfig;
|
||||||
sessionContext.Next = session;
|
sessionContext.Next = session;
|
||||||
|
sessionContext.Next.AppConfig = appConfig;
|
||||||
session.Settings = settings;
|
session.Settings = settings;
|
||||||
settings.ServicePolicy = ServicePolicy.Mandatory;
|
settings.ServicePolicy = ServicePolicy.Mandatory;
|
||||||
|
|
||||||
|
@ -71,10 +81,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
{
|
{
|
||||||
service.SetupGet(s => s.IsConnected).Returns(true);
|
service.SetupGet(s => s.IsConnected).Returns(true);
|
||||||
service.Setup(s => s.Connect(null, true)).Returns(true);
|
service.Setup(s => s.Connect(null, true)).Returns(true);
|
||||||
service
|
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
|
||||||
.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStarted += null));
|
|
||||||
|
|
||||||
var result = sut.Perform();
|
var result = sut.Perform();
|
||||||
|
|
||||||
|
@ -88,10 +95,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
{
|
{
|
||||||
service.SetupGet(s => s.IsConnected).Returns(true);
|
service.SetupGet(s => s.IsConnected).Returns(true);
|
||||||
service.Setup(s => s.Connect(null, true)).Returns(true);
|
service.Setup(s => s.Connect(null, true)).Returns(true);
|
||||||
service
|
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true));
|
||||||
.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceFailed += null));
|
|
||||||
|
|
||||||
var result = sut.Perform();
|
var result = sut.Perform();
|
||||||
|
|
||||||
|
@ -197,10 +201,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Repeat_MustStopCurrentAndStartNewSession()
|
public void Repeat_MustStopCurrentAndStartNewSession()
|
||||||
{
|
{
|
||||||
service
|
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
|
||||||
.Setup(s => s.StopSession(It.IsAny<Guid>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStopped += null));
|
|
||||||
|
|
||||||
PerformNormally();
|
PerformNormally();
|
||||||
|
|
||||||
|
@ -222,10 +223,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
service.Reset();
|
service.Reset();
|
||||||
service.SetupGet(s => s.IsConnected).Returns(false);
|
service.SetupGet(s => s.IsConnected).Returns(false);
|
||||||
service.Setup(s => s.Connect(null, true)).Returns(true);
|
service.Setup(s => s.Connect(null, true)).Returns(true);
|
||||||
service
|
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
|
||||||
.Setup(s => s.StopSession(It.IsAny<Guid>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStopped += null));
|
|
||||||
|
|
||||||
var result = sut.Repeat();
|
var result = sut.Repeat();
|
||||||
|
|
||||||
|
@ -251,58 +249,49 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Revert_MustDisconnect()
|
public void Repeat_MustFailIfSessionNotStoppedWithinTimeout()
|
||||||
{
|
|
||||||
service.Setup(s => s.Disconnect()).Returns(true).Callback(() => runtimeHost.Raise(h => h.ServiceDisconnected += null));
|
|
||||||
service
|
|
||||||
.Setup(s => s.StopSession(It.IsAny<Guid>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStopped += null));
|
|
||||||
|
|
||||||
PerformNormally();
|
|
||||||
|
|
||||||
var result = sut.Revert();
|
|
||||||
|
|
||||||
service.Verify(s => s.Disconnect(), Times.Once);
|
|
||||||
Assert.AreEqual(OperationResult.Success, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void Revert_MustFailIfServiceNotDisconnectedWithinTimeout()
|
|
||||||
{
|
{
|
||||||
const int TIMEOUT = 50;
|
const int TIMEOUT = 50;
|
||||||
|
|
||||||
var after = default(DateTime);
|
var after = default(DateTime);
|
||||||
var before = default(DateTime);
|
var before = default(DateTime);
|
||||||
|
|
||||||
|
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
|
||||||
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, sessionContext, TIMEOUT);
|
sut = new ServiceOperation(logger.Object, runtimeHost.Object, service.Object, sessionContext, TIMEOUT);
|
||||||
|
|
||||||
service.Setup(s => s.Disconnect()).Returns(true);
|
|
||||||
service
|
|
||||||
.Setup(s => s.StopSession(It.IsAny<Guid>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStopped += null));
|
|
||||||
|
|
||||||
PerformNormally();
|
PerformNormally();
|
||||||
|
|
||||||
before = DateTime.Now;
|
before = DateTime.Now;
|
||||||
var result = sut.Revert();
|
var result = sut.Repeat();
|
||||||
after = DateTime.Now;
|
after = DateTime.Now;
|
||||||
|
|
||||||
service.Verify(s => s.Disconnect(), Times.Once);
|
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Once);
|
||||||
|
service.Verify(s => s.Disconnect(), Times.Never);
|
||||||
|
|
||||||
Assert.AreEqual(OperationResult.Failed, result);
|
Assert.AreEqual(OperationResult.Failed, result);
|
||||||
Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
|
Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Revert_MustDisconnect()
|
||||||
|
{
|
||||||
|
service.Setup(s => s.Disconnect()).Returns(true);
|
||||||
|
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
|
||||||
|
|
||||||
|
PerformNormally();
|
||||||
|
|
||||||
|
var result = sut.Revert();
|
||||||
|
|
||||||
|
service.Verify(s => s.Disconnect(), Times.Once);
|
||||||
|
|
||||||
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Revert_MustStopSessionIfConnected()
|
public void Revert_MustStopSessionIfConnected()
|
||||||
{
|
{
|
||||||
service.Setup(s => s.Disconnect()).Returns(true).Callback(() => runtimeHost.Raise(h => h.ServiceDisconnected += null));
|
service.Setup(s => s.Disconnect()).Returns(true);
|
||||||
service
|
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
|
||||||
.Setup(s => s.StopSession(It.IsAny<Guid>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStopped += null));
|
|
||||||
|
|
||||||
PerformNormally();
|
PerformNormally();
|
||||||
|
|
||||||
|
@ -332,10 +321,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Revert_MustFailIfSessionStopUnsuccessful()
|
public void Revert_MustFailIfSessionStopUnsuccessful()
|
||||||
{
|
{
|
||||||
service
|
service.Setup(s => s.StopSession(It.IsAny<Guid>())).Returns(new CommunicationResult(true));
|
||||||
.Setup(s => s.StopSession(It.IsAny<Guid>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceFailed += null));
|
|
||||||
|
|
||||||
PerformNormally();
|
PerformNormally();
|
||||||
|
|
||||||
|
@ -377,6 +363,20 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
Assert.AreEqual(OperationResult.Success, result);
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void Revert_MustNotStopSessionIfNoSessionRunning()
|
||||||
|
{
|
||||||
|
sessionContext.Current = null;
|
||||||
|
service.SetupGet(s => s.IsConnected).Returns(true);
|
||||||
|
service.Setup(s => s.Disconnect()).Returns(true);
|
||||||
|
|
||||||
|
var result = sut.Revert();
|
||||||
|
|
||||||
|
service.Verify(s => s.StopSession(It.IsAny<Guid>()), Times.Never);
|
||||||
|
|
||||||
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Revert_MustNotDisconnnectIfNotConnected()
|
public void Revert_MustNotDisconnnectIfNotConnected()
|
||||||
{
|
{
|
||||||
|
@ -390,10 +390,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
{
|
{
|
||||||
service.SetupGet(s => s.IsConnected).Returns(true);
|
service.SetupGet(s => s.IsConnected).Returns(true);
|
||||||
service.Setup(s => s.Connect(null, true)).Returns(true);
|
service.Setup(s => s.Connect(null, true)).Returns(true);
|
||||||
service
|
service.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>())).Returns(new CommunicationResult(true)).Callback(() => serviceEvent.Set());
|
||||||
.Setup(s => s.StartSession(It.IsAny<ServiceConfiguration>()))
|
|
||||||
.Returns(new CommunicationResult(true))
|
|
||||||
.Callback(() => runtimeHost.Raise(h => h.ServiceSessionStarted += null));
|
|
||||||
|
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Runtime.Operations;
|
using SafeExamBrowser.Runtime.Operations;
|
||||||
|
|
||||||
|
@ -52,33 +53,41 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
session.ClientAuthenticationToken = token;
|
session.ClientAuthenticationToken = token;
|
||||||
|
|
||||||
sut.Perform();
|
var result = sut.Perform();
|
||||||
|
|
||||||
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
|
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
|
||||||
runtimeHost.VerifySet(r => r.AuthenticationToken = token, Times.Once);
|
|
||||||
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
|
Assert.IsNull(sessionContext.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustInitializeConfigurationOnRepeat()
|
public void MustInitializeConfigurationOnRepeat()
|
||||||
{
|
{
|
||||||
|
var currentSession = new SessionConfiguration();
|
||||||
var token = Guid.NewGuid();
|
var token = Guid.NewGuid();
|
||||||
|
|
||||||
session.ClientAuthenticationToken = token;
|
session.ClientAuthenticationToken = token;
|
||||||
|
sessionContext.Current = currentSession;
|
||||||
|
|
||||||
sut.Repeat();
|
var result = sut.Repeat();
|
||||||
|
|
||||||
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
|
configuration.Verify(c => c.InitializeSessionConfiguration(), Times.Once);
|
||||||
runtimeHost.VerifySet(r => r.AuthenticationToken = token, Times.Once);
|
|
||||||
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
|
Assert.AreSame(currentSession,sessionContext.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustDoNothingOnRevert()
|
public void MustDoNothingOnRevert()
|
||||||
{
|
{
|
||||||
sut.Revert();
|
var result = sut.Revert();
|
||||||
|
|
||||||
configuration.VerifyNoOtherCalls();
|
configuration.VerifyNoOtherCalls();
|
||||||
logger.VerifyNoOtherCalls();
|
logger.VerifyNoOtherCalls();
|
||||||
runtimeHost.VerifyNoOtherCalls();
|
runtimeHost.VerifyNoOtherCalls();
|
||||||
|
|
||||||
|
Assert.AreEqual(OperationResult.Success, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 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/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
|
||||||
using SafeExamBrowser.Logging;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Runtime.Communication
|
|
||||||
{
|
|
||||||
internal class ProxyFactory : IProxyFactory
|
|
||||||
{
|
|
||||||
private IProxyObjectFactory factory;
|
|
||||||
private ILogger logger;
|
|
||||||
|
|
||||||
public ProxyFactory(IProxyObjectFactory factory, ILogger logger)
|
|
||||||
{
|
|
||||||
this.factory = factory;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IClientProxy CreateClientProxy(string address)
|
|
||||||
{
|
|
||||||
return new ClientProxy(address, factory, new ModuleLogger(logger, nameof(ClientProxy)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
using SafeExamBrowser.Contracts.Communication.Events;
|
using SafeExamBrowser.Contracts.Communication.Events;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
|
@ -18,7 +19,7 @@ namespace SafeExamBrowser.Runtime.Communication
|
||||||
internal class RuntimeHost : BaseHost, IRuntimeHost
|
internal class RuntimeHost : BaseHost, IRuntimeHost
|
||||||
{
|
{
|
||||||
public bool AllowConnection { get; set; }
|
public bool AllowConnection { get; set; }
|
||||||
public Guid AuthenticationToken { private get; set; }
|
public Guid? AuthenticationToken { private get; set; }
|
||||||
|
|
||||||
public event CommunicationEventHandler ClientDisconnected;
|
public event CommunicationEventHandler ClientDisconnected;
|
||||||
public event CommunicationEventHandler ClientReady;
|
public event CommunicationEventHandler ClientReady;
|
||||||
|
@ -26,10 +27,6 @@ namespace SafeExamBrowser.Runtime.Communication
|
||||||
public event CommunicationEventHandler<MessageBoxReplyEventArgs> MessageBoxReplyReceived;
|
public event CommunicationEventHandler<MessageBoxReplyEventArgs> MessageBoxReplyReceived;
|
||||||
public event CommunicationEventHandler<PasswordReplyEventArgs> PasswordReceived;
|
public event CommunicationEventHandler<PasswordReplyEventArgs> PasswordReceived;
|
||||||
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
|
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
|
||||||
public event CommunicationEventHandler ServiceDisconnected;
|
|
||||||
public event CommunicationEventHandler ServiceFailed;
|
|
||||||
public event CommunicationEventHandler ServiceSessionStarted;
|
|
||||||
public event CommunicationEventHandler ServiceSessionStopped;
|
|
||||||
public event CommunicationEventHandler ShutdownRequested;
|
public event CommunicationEventHandler ShutdownRequested;
|
||||||
|
|
||||||
public RuntimeHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms) : base(address, factory, logger, timeout_ms)
|
public RuntimeHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms) : base(address, factory, logger, timeout_ms)
|
||||||
|
@ -38,7 +35,7 @@ namespace SafeExamBrowser.Runtime.Communication
|
||||||
|
|
||||||
protected override bool OnConnect(Guid? token = null)
|
protected override bool OnConnect(Guid? token = null)
|
||||||
{
|
{
|
||||||
var authenticated = AuthenticationToken == token;
|
var authenticated = AuthenticationToken.HasValue && AuthenticationToken == token;
|
||||||
var accepted = AllowConnection && authenticated;
|
var accepted = AllowConnection && authenticated;
|
||||||
|
|
||||||
if (accepted)
|
if (accepted)
|
||||||
|
@ -49,9 +46,12 @@ namespace SafeExamBrowser.Runtime.Communication
|
||||||
return accepted;
|
return accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisconnect()
|
protected override void OnDisconnect(Interlocutor interlocutor)
|
||||||
{
|
{
|
||||||
ClientDisconnected?.Invoke();
|
if (interlocutor == Interlocutor.Client)
|
||||||
|
{
|
||||||
|
ClientDisconnected?.Invoke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Response OnReceive(Message message)
|
protected override Response OnReceive(Message message)
|
||||||
|
|
|
@ -17,6 +17,7 @@ using SafeExamBrowser.Configuration.Cryptography;
|
||||||
using SafeExamBrowser.Configuration.DataCompression;
|
using SafeExamBrowser.Configuration.DataCompression;
|
||||||
using SafeExamBrowser.Configuration.DataFormats;
|
using SafeExamBrowser.Configuration.DataFormats;
|
||||||
using SafeExamBrowser.Configuration.DataResources;
|
using SafeExamBrowser.Configuration.DataResources;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
using SafeExamBrowser.Contracts.I18n;
|
using SafeExamBrowser.Contracts.I18n;
|
||||||
|
@ -65,9 +66,9 @@ namespace SafeExamBrowser.Runtime
|
||||||
var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory)));
|
var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory)));
|
||||||
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||||
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
||||||
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger);
|
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), ModuleLogger(nameof(ProxyFactory)));
|
||||||
var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), ModuleLogger(nameof(RuntimeHost)), FIVE_SECONDS);
|
var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), ModuleLogger(nameof(RuntimeHost)), FIVE_SECONDS);
|
||||||
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy)));
|
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy)), Interlocutor.Runtime);
|
||||||
var sessionContext = new SessionContext();
|
var sessionContext = new SessionContext();
|
||||||
var uiFactory = new UserInterfaceFactory(text);
|
var uiFactory = new UserInterfaceFactory(text);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Events;
|
using SafeExamBrowser.Contracts.Communication.Events;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
|
@ -111,6 +113,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
logger.Info("Starting new client process...");
|
logger.Info("Starting new client process...");
|
||||||
runtimeHost.AllowConnection = true;
|
runtimeHost.AllowConnection = true;
|
||||||
|
runtimeHost.AuthenticationToken = Context.Next.ClientAuthenticationToken;
|
||||||
runtimeHost.ClientReady += clientReadyEventHandler;
|
runtimeHost.ClientReady += clientReadyEventHandler;
|
||||||
ClientProcess = processFactory.StartNew(executablePath, logFilePath, logLevel, runtimeHostUri, authenticationToken, uiMode);
|
ClientProcess = processFactory.StartNew(executablePath, logFilePath, logLevel, runtimeHostUri, authenticationToken, uiMode);
|
||||||
ClientProcess.Terminated += clientTerminatedEventHandler;
|
ClientProcess.Terminated += clientTerminatedEventHandler;
|
||||||
|
@ -119,6 +122,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
clientReady = clientReadyEvent.WaitOne(timeout_ms);
|
clientReady = clientReadyEvent.WaitOne(timeout_ms);
|
||||||
|
|
||||||
runtimeHost.AllowConnection = false;
|
runtimeHost.AllowConnection = false;
|
||||||
|
runtimeHost.AuthenticationToken = default(Guid?);
|
||||||
runtimeHost.ClientReady -= clientReadyEventHandler;
|
runtimeHost.ClientReady -= clientReadyEventHandler;
|
||||||
ClientProcess.Terminated -= clientTerminatedEventHandler;
|
ClientProcess.Terminated -= clientTerminatedEventHandler;
|
||||||
|
|
||||||
|
@ -145,7 +149,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
var success = false;
|
var success = false;
|
||||||
|
|
||||||
logger.Info("Client has been successfully started and initialized. Creating communication proxy for client host...");
|
logger.Info("Client has been successfully started and initialized. Creating communication proxy for client host...");
|
||||||
ClientProxy = proxyFactory.CreateClientProxy(Context.Next.AppConfig.ClientAddress);
|
ClientProxy = proxyFactory.CreateClientProxy(Context.Next.AppConfig.ClientAddress, Interlocutor.Runtime);
|
||||||
|
|
||||||
if (ClientProxy.Connect(Context.Next.ClientAuthenticationToken))
|
if (ClientProxy.Connect(Context.Next.ClientAuthenticationToken))
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Security.AccessControl;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using SafeExamBrowser.Contracts.Communication.Events;
|
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Communication.Proxies;
|
using SafeExamBrowser.Contracts.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
@ -46,7 +47,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
public override OperationResult Perform()
|
public override OperationResult Perform()
|
||||||
{
|
{
|
||||||
logger.Info($"Initializing service session...");
|
logger.Info($"Initializing service...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
|
||||||
|
|
||||||
var success = TryEstablishConnection();
|
var success = TryEstablishConnection();
|
||||||
|
@ -61,7 +62,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
public override OperationResult Repeat()
|
public override OperationResult Repeat()
|
||||||
{
|
{
|
||||||
logger.Info($"Initializing new service session...");
|
logger.Info($"Initializing service...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
|
||||||
|
|
||||||
var success = false;
|
var success = false;
|
||||||
|
@ -85,14 +86,18 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
public override OperationResult Revert()
|
public override OperationResult Revert()
|
||||||
{
|
{
|
||||||
logger.Info("Finalizing service session...");
|
logger.Info("Finalizing service...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServiceSession);
|
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServiceSession);
|
||||||
|
|
||||||
var success = true;
|
var success = true;
|
||||||
|
|
||||||
if (service.IsConnected)
|
if (service.IsConnected)
|
||||||
{
|
{
|
||||||
success &= TryStopSession();
|
if (Context.Current != null)
|
||||||
|
{
|
||||||
|
success &= TryStopSession();
|
||||||
|
}
|
||||||
|
|
||||||
success &= TryTerminateConnection();
|
success &= TryTerminateConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,36 +142,18 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
private bool TryTerminateConnection()
|
private bool TryTerminateConnection()
|
||||||
{
|
{
|
||||||
var serviceEvent = new AutoResetEvent(false);
|
var disconnected = service.Disconnect();
|
||||||
var serviceEventHandler = new CommunicationEventHandler(() => serviceEvent.Set());
|
|
||||||
|
|
||||||
runtimeHost.ServiceDisconnected += serviceEventHandler;
|
if (disconnected)
|
||||||
|
|
||||||
var success = service.Disconnect();
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
{
|
||||||
logger.Info("Successfully disconnected from service. Waiting for service to disconnect...");
|
logger.Info("Successfully disconnected from service.");
|
||||||
|
|
||||||
success = serviceEvent.WaitOne(timeout_ms);
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
logger.Info("Service disconnected successfully.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.Error($"Service failed to disconnect within {timeout_ms / 1000} seconds!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Error("Failed to disconnect from service!");
|
logger.Error("Failed to disconnect from service!");
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeHost.ServiceDisconnected -= serviceEventHandler;
|
return disconnected;
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryStartSession()
|
private bool TryStartSession()
|
||||||
|
@ -174,18 +161,10 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
var configuration = new ServiceConfiguration
|
var configuration = new ServiceConfiguration
|
||||||
{
|
{
|
||||||
AppConfig = Context.Next.AppConfig,
|
AppConfig = Context.Next.AppConfig,
|
||||||
AuthenticationToken = Context.Next.ServiceAuthenticationToken,
|
|
||||||
SessionId = Context.Next.SessionId,
|
SessionId = Context.Next.SessionId,
|
||||||
Settings = Context.Next.Settings
|
Settings = Context.Next.Settings
|
||||||
};
|
};
|
||||||
var failure = false;
|
var started = false;
|
||||||
var success = false;
|
|
||||||
var serviceEvent = new AutoResetEvent(false);
|
|
||||||
var failureEventHandler = new CommunicationEventHandler(() => { failure = true; serviceEvent.Set(); });
|
|
||||||
var successEventHandler = new CommunicationEventHandler(() => { success = true; serviceEvent.Set(); });
|
|
||||||
|
|
||||||
runtimeHost.ServiceFailed += failureEventHandler;
|
|
||||||
runtimeHost.ServiceSessionStarted += successEventHandler;
|
|
||||||
|
|
||||||
logger.Info("Starting new service session...");
|
logger.Info("Starting new service session...");
|
||||||
|
|
||||||
|
@ -193,16 +172,12 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
if (communication.Success)
|
if (communication.Success)
|
||||||
{
|
{
|
||||||
serviceEvent.WaitOne(timeout_ms);
|
started = TryWaitForServiceEvent(Context.Next.AppConfig.ServiceEventName);
|
||||||
|
|
||||||
if (success)
|
if (started)
|
||||||
{
|
{
|
||||||
logger.Info("Successfully started new service session.");
|
logger.Info("Successfully started new service session.");
|
||||||
}
|
}
|
||||||
else if (failure)
|
|
||||||
{
|
|
||||||
logger.Error("An error occurred while attempting to start a new service session! Please check the service log for further information.");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Error($"Failed to start new service session within {timeout_ms / 1000} seconds!");
|
logger.Error($"Failed to start new service session within {timeout_ms / 1000} seconds!");
|
||||||
|
@ -213,22 +188,12 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
logger.Error("Failed to communicate session start to service!");
|
logger.Error("Failed to communicate session start to service!");
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeHost.ServiceFailed -= failureEventHandler;
|
return started;
|
||||||
runtimeHost.ServiceSessionStarted -= successEventHandler;
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryStopSession()
|
private bool TryStopSession()
|
||||||
{
|
{
|
||||||
var failure = false;
|
var stopped = false;
|
||||||
var success = false;
|
|
||||||
var serviceEvent = new AutoResetEvent(false);
|
|
||||||
var failureEventHandler = new CommunicationEventHandler(() => { failure = true; serviceEvent.Set(); });
|
|
||||||
var successEventHandler = new CommunicationEventHandler(() => { success = true; serviceEvent.Set(); });
|
|
||||||
|
|
||||||
runtimeHost.ServiceFailed += failureEventHandler;
|
|
||||||
runtimeHost.ServiceSessionStopped += successEventHandler;
|
|
||||||
|
|
||||||
logger.Info("Stopping current service session...");
|
logger.Info("Stopping current service session...");
|
||||||
|
|
||||||
|
@ -236,16 +201,12 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
if (communication.Success)
|
if (communication.Success)
|
||||||
{
|
{
|
||||||
serviceEvent.WaitOne(timeout_ms);
|
stopped = TryWaitForServiceEvent(Context.Current.AppConfig.ServiceEventName);
|
||||||
|
|
||||||
if (success)
|
if (stopped)
|
||||||
{
|
{
|
||||||
logger.Info("Successfully stopped service session.");
|
logger.Info("Successfully stopped service session.");
|
||||||
}
|
}
|
||||||
else if (failure)
|
|
||||||
{
|
|
||||||
logger.Error("An error occurred while attempting to stop the current service session! Please check the service log for further information.");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Error($"Failed to stop service session within {timeout_ms / 1000} seconds!");
|
logger.Error($"Failed to stop service session within {timeout_ms / 1000} seconds!");
|
||||||
|
@ -256,10 +217,31 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
logger.Error("Failed to communicate session stop to service!");
|
logger.Error("Failed to communicate session stop to service!");
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeHost.ServiceFailed -= failureEventHandler;
|
return stopped;
|
||||||
runtimeHost.ServiceSessionStopped -= successEventHandler;
|
}
|
||||||
|
|
||||||
return success;
|
private bool TryWaitForServiceEvent(string eventName)
|
||||||
|
{
|
||||||
|
var serviceEvent = default(EventWaitHandle);
|
||||||
|
var startTime = DateTime.Now;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (EventWaitHandle.TryOpenExisting(eventName, EventWaitHandleRights.Synchronize, out serviceEvent))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (startTime.AddMilliseconds(timeout_ms) > DateTime.Now);
|
||||||
|
|
||||||
|
if (serviceEvent != default(EventWaitHandle))
|
||||||
|
{
|
||||||
|
using (serviceEvent)
|
||||||
|
{
|
||||||
|
return serviceEvent.WaitOne(timeout_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
@ -63,7 +62,6 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeSession);
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeSession);
|
||||||
|
|
||||||
Context.Next = configuration.InitializeSessionConfiguration();
|
Context.Next = configuration.InitializeSessionConfiguration();
|
||||||
runtimeHost.AuthenticationToken = Context.Next.ClientAuthenticationToken;
|
|
||||||
|
|
||||||
logger.Info($" -> Client-ID: {Context.Next.AppConfig.ClientId}");
|
logger.Info($" -> Client-ID: {Context.Next.AppConfig.ClientId}");
|
||||||
logger.Info($" -> Runtime-ID: {Context.Next.AppConfig.RuntimeId}");
|
logger.Info($" -> Runtime-ID: {Context.Next.AppConfig.RuntimeId}");
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Runtime.Operations
|
namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
|
@ -103,7 +103,6 @@
|
||||||
<Compile Include="Operations\SessionActivationOperation.cs" />
|
<Compile Include="Operations\SessionActivationOperation.cs" />
|
||||||
<Compile Include="Operations\SessionOperation.cs" />
|
<Compile Include="Operations\SessionOperation.cs" />
|
||||||
<Compile Include="Operations\SessionInitializationOperation.cs" />
|
<Compile Include="Operations\SessionInitializationOperation.cs" />
|
||||||
<Compile Include="Communication\ProxyFactory.cs" />
|
|
||||||
<Compile Include="Communication\RuntimeHost.cs" />
|
<Compile Include="Communication\RuntimeHost.cs" />
|
||||||
<Compile Include="CompositionRoot.cs" />
|
<Compile Include="CompositionRoot.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs">
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
|
|
|
@ -11,12 +11,14 @@ using Moq;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Service.UnitTests
|
namespace SafeExamBrowser.Service.UnitTests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class ServiceControllerTests
|
public class ServiceControllerTests
|
||||||
{
|
{
|
||||||
|
private Mock<ILogger> logger;
|
||||||
private Mock<IOperationSequence> bootstrapSequence;
|
private Mock<IOperationSequence> bootstrapSequence;
|
||||||
private SessionContext sessionContext;
|
private SessionContext sessionContext;
|
||||||
private Mock<IRepeatableOperationSequence> sessionSequence;
|
private Mock<IRepeatableOperationSequence> sessionSequence;
|
||||||
|
@ -26,12 +28,13 @@ namespace SafeExamBrowser.Service.UnitTests
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
logger = new Mock<ILogger>();
|
||||||
bootstrapSequence = new Mock<IOperationSequence>();
|
bootstrapSequence = new Mock<IOperationSequence>();
|
||||||
sessionContext = new SessionContext();
|
sessionContext = new SessionContext();
|
||||||
sessionSequence = new Mock<IRepeatableOperationSequence>();
|
sessionSequence = new Mock<IRepeatableOperationSequence>();
|
||||||
serviceHost = new Mock<IServiceHost>();
|
serviceHost = new Mock<IServiceHost>();
|
||||||
|
|
||||||
sut = new ServiceController(bootstrapSequence.Object, sessionSequence.Object, serviceHost.Object, sessionContext);
|
sut = new ServiceController(logger.Object, bootstrapSequence.Object, sessionSequence.Object, serviceHost.Object, sessionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
@ -39,7 +42,7 @@ namespace SafeExamBrowser.Service.UnitTests
|
||||||
{
|
{
|
||||||
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
|
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
|
||||||
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
|
sessionSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
|
||||||
sessionContext.Current = null;
|
sessionContext.Configuration = null;
|
||||||
|
|
||||||
var success = sut.TryStart();
|
var success = sut.TryStart();
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ namespace SafeExamBrowser.Service.UnitTests
|
||||||
|
|
||||||
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
|
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
|
||||||
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
|
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
|
||||||
sessionContext.Current = new ServiceConfiguration();
|
sessionContext.Configuration = new ServiceConfiguration();
|
||||||
|
|
||||||
sut.Terminate();
|
sut.Terminate();
|
||||||
|
|
||||||
|
@ -94,7 +97,7 @@ namespace SafeExamBrowser.Service.UnitTests
|
||||||
|
|
||||||
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
|
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
|
||||||
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
|
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
|
||||||
sessionContext.Current = null;
|
sessionContext.Configuration = null;
|
||||||
|
|
||||||
sut.Terminate();
|
sut.Terminate();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
|
using SafeExamBrowser.Contracts.Communication;
|
||||||
using SafeExamBrowser.Contracts.Communication.Data;
|
using SafeExamBrowser.Contracts.Communication.Data;
|
||||||
|
using SafeExamBrowser.Contracts.Communication.Events;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
@ -16,28 +18,68 @@ namespace SafeExamBrowser.Service.Communication
|
||||||
{
|
{
|
||||||
internal class ServiceHost : BaseHost, IServiceHost
|
internal class ServiceHost : BaseHost, IServiceHost
|
||||||
{
|
{
|
||||||
|
private readonly object @lock = new object();
|
||||||
|
|
||||||
|
private bool allowConnection;
|
||||||
|
|
||||||
|
public bool AllowConnection
|
||||||
|
{
|
||||||
|
get { lock (@lock) { return allowConnection; } }
|
||||||
|
set { lock (@lock) { allowConnection = value; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public event CommunicationEventHandler<SessionStartEventArgs> SessionStartRequested;
|
||||||
|
public event CommunicationEventHandler<SessionStopEventArgs> SessionStopRequested;
|
||||||
|
|
||||||
internal ServiceHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms) : base(address, factory, logger, timeout_ms)
|
internal ServiceHost(string address, IHostObjectFactory factory, ILogger logger, int timeout_ms) : base(address, factory, logger, timeout_ms)
|
||||||
{
|
{
|
||||||
|
AllowConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnConnect(Guid? token)
|
protected override bool OnConnect(Guid? token)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
lock (@lock)
|
||||||
|
{
|
||||||
|
var allow = AllowConnection;
|
||||||
|
|
||||||
|
if (allow)
|
||||||
|
{
|
||||||
|
AllowConnection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisconnect()
|
protected override void OnDisconnect(Interlocutor interlocutor)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (interlocutor == Interlocutor.Runtime)
|
||||||
|
{
|
||||||
|
lock (@lock)
|
||||||
|
{
|
||||||
|
AllowConnection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Response OnReceive(Message message)
|
protected override Response OnReceive(Message message)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
switch (message)
|
||||||
|
{
|
||||||
|
case SessionStartMessage m:
|
||||||
|
SessionStartRequested?.InvokeAsync(new SessionStartEventArgs { Configuration = m.Configuration });
|
||||||
|
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||||
|
case SessionStopMessage m:
|
||||||
|
SessionStopRequested?.InvokeAsync(new SessionStopEventArgs { SessionId = m.SessionId });
|
||||||
|
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Response OnReceive(SimpleMessagePurport message)
|
protected override Response OnReceive(SimpleMessagePurport message)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Contracts.Service;
|
using SafeExamBrowser.Contracts.Service;
|
||||||
|
@ -17,6 +18,7 @@ using SafeExamBrowser.Core.OperationModel;
|
||||||
using SafeExamBrowser.Core.Operations;
|
using SafeExamBrowser.Core.Operations;
|
||||||
using SafeExamBrowser.Logging;
|
using SafeExamBrowser.Logging;
|
||||||
using SafeExamBrowser.Service.Communication;
|
using SafeExamBrowser.Service.Communication;
|
||||||
|
using SafeExamBrowser.Service.Operations;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Service
|
namespace SafeExamBrowser.Service
|
||||||
{
|
{
|
||||||
|
@ -33,25 +35,26 @@ namespace SafeExamBrowser.Service
|
||||||
|
|
||||||
InitializeLogging();
|
InitializeLogging();
|
||||||
|
|
||||||
|
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ProxyFactory)));
|
||||||
var serviceHost = new ServiceHost(SERVICE_ADDRESS, new HostObjectFactory(), new ModuleLogger(logger, nameof(ServiceHost)), FIVE_SECONDS);
|
var serviceHost = new ServiceHost(SERVICE_ADDRESS, new HostObjectFactory(), new ModuleLogger(logger, nameof(ServiceHost)), FIVE_SECONDS);
|
||||||
var sessionContext = new SessionContext();
|
var sessionContext = new SessionContext();
|
||||||
|
|
||||||
var bootstrapOperations = new Queue<IOperation>();
|
var bootstrapOperations = new Queue<IOperation>();
|
||||||
var sessionOperations = new Queue<IRepeatableOperation>();
|
var sessionOperations = new Queue<IOperation>();
|
||||||
|
|
||||||
// TODO: bootstrapOperations.Enqueue(new RestoreOperation());
|
// TODO: bootstrapOperations.Enqueue(new RestoreOperation());
|
||||||
bootstrapOperations.Enqueue(new CommunicationHostOperation(serviceHost, logger));
|
bootstrapOperations.Enqueue(new CommunicationHostOperation(serviceHost, logger));
|
||||||
|
bootstrapOperations.Enqueue(new ServiceEventCleanupOperation(logger, sessionContext));
|
||||||
|
|
||||||
// sessionOperations.Enqueue(new RuntimeConnectionOperation());
|
sessionOperations.Enqueue(new SessionInitializationOperation(logger, serviceHost, sessionContext));
|
||||||
// sessionOperations.Enqueue(new LogOperation());
|
// TODO: sessionOperations.Enqueue(new RegistryOperation());
|
||||||
// sessionOperations.Enqueue(new RegistryOperation());
|
// sessionOperations.Enqueue(new WindowsUpdateOperation());
|
||||||
// sessionOperations.Enqueue(new WindowsUpdateOperation());
|
sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));
|
||||||
// sessionOperations.Enqueue(new SessionActivationOperation());
|
|
||||||
|
|
||||||
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
|
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
|
||||||
var sessionSequence = new RepeatableOperationSequence(logger, sessionOperations);
|
var sessionSequence = new OperationSequence(logger, sessionOperations);
|
||||||
|
|
||||||
ServiceController = new ServiceController(bootstrapSequence, sessionSequence, serviceHost, sessionContext);
|
ServiceController = new ServiceController(logger, bootstrapSequence, sessionSequence, serviceHost, sessionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void LogStartupInformation()
|
internal void LogStartupInformation()
|
||||||
|
@ -62,7 +65,6 @@ namespace SafeExamBrowser.Service
|
||||||
|
|
||||||
internal void LogShutdownInformation()
|
internal void LogShutdownInformation()
|
||||||
{
|
{
|
||||||
logger?.Log(string.Empty);
|
|
||||||
logger?.Log($"# Service terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
logger?.Log($"# Service terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Service.Operations
|
||||||
|
{
|
||||||
|
internal class ServiceEventCleanupOperation : IOperation
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
private SessionContext sessionContext;
|
||||||
|
|
||||||
|
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||||
|
public event StatusChangedEventHandler StatusChanged { add { } remove { } }
|
||||||
|
|
||||||
|
public ServiceEventCleanupOperation(ILogger logger, SessionContext sessionContext)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.sessionContext = sessionContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationResult Perform()
|
||||||
|
{
|
||||||
|
return OperationResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationResult Revert()
|
||||||
|
{
|
||||||
|
if (sessionContext.EventWaitHandle != null)
|
||||||
|
{
|
||||||
|
logger.Info("Closing service event...");
|
||||||
|
sessionContext.EventWaitHandle.Close();
|
||||||
|
logger.Info("Service event successfully closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return OperationResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Service.Operations
|
||||||
|
{
|
||||||
|
internal class SessionActivationOperation : SessionOperation
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
public SessionActivationOperation(ILogger logger, SessionContext sessionContext) : base(sessionContext)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override OperationResult Perform()
|
||||||
|
{
|
||||||
|
var success = Context.EventWaitHandle.Set();
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Info("Successfully informed runtime about new session activation.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Failed to inform runtime about new session activation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success ? OperationResult.Success : OperationResult.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override OperationResult Revert()
|
||||||
|
{
|
||||||
|
return OperationResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Threading;
|
||||||
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Service.Operations
|
||||||
|
{
|
||||||
|
internal class SessionInitializationOperation : SessionOperation
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
private IServiceHost serviceHost;
|
||||||
|
|
||||||
|
public SessionInitializationOperation(ILogger logger, IServiceHost serviceHost, SessionContext sessionContext) : base(sessionContext)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.serviceHost = serviceHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override OperationResult Perform()
|
||||||
|
{
|
||||||
|
logger.Info("Initializing new session...");
|
||||||
|
|
||||||
|
serviceHost.AllowConnection = false;
|
||||||
|
logger.Info($" -> Client-ID: {Context.Configuration.AppConfig.ClientId}");
|
||||||
|
logger.Info($" -> Runtime-ID: {Context.Configuration.AppConfig.RuntimeId}");
|
||||||
|
logger.Info($" -> Session-ID: {Context.Configuration.SessionId}");
|
||||||
|
|
||||||
|
InitializeEventWaitHandle();
|
||||||
|
|
||||||
|
return OperationResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override OperationResult Revert()
|
||||||
|
{
|
||||||
|
logger.Info("Finalizing current session...");
|
||||||
|
|
||||||
|
var success = Context.EventWaitHandle?.Set() == true;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Info("Successfully informed runtime about session termination.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error("Failed to inform runtime about session termination!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.Configuration = null;
|
||||||
|
serviceHost.AllowConnection = true;
|
||||||
|
|
||||||
|
return success ? OperationResult.Success : OperationResult.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeEventWaitHandle()
|
||||||
|
{
|
||||||
|
var securityIdentifier = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
|
||||||
|
var accessRule = new EventWaitHandleAccessRule(securityIdentifier, EventWaitHandleRights.Synchronize, AccessControlType.Allow);
|
||||||
|
var security = new EventWaitHandleSecurity();
|
||||||
|
|
||||||
|
security.AddAccessRule(accessRule);
|
||||||
|
|
||||||
|
if (Context.EventWaitHandle != null)
|
||||||
|
{
|
||||||
|
logger.Info("Closing service event from previous session...");
|
||||||
|
Context.EventWaitHandle.Close();
|
||||||
|
logger.Info("Service event successfully closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("Attempting to create new service event...");
|
||||||
|
Context.EventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Context.Configuration.AppConfig.ServiceEventName, out _, security);
|
||||||
|
logger.Info("Service event successfully created.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
SafeExamBrowser.Service/Operations/SessionOperation.cs
Normal file
32
SafeExamBrowser.Service/Operations/SessionOperation.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Service.Operations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The base implementation to be used for all operations in the session operation sequence.
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class SessionOperation : IOperation
|
||||||
|
{
|
||||||
|
protected SessionContext Context { get; private set; }
|
||||||
|
|
||||||
|
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||||
|
public event StatusChangedEventHandler StatusChanged { add { } remove { } }
|
||||||
|
|
||||||
|
public SessionOperation(SessionContext sessionContext)
|
||||||
|
{
|
||||||
|
Context = sessionContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract OperationResult Perform();
|
||||||
|
public abstract OperationResult Revert();
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,6 +64,10 @@
|
||||||
<Compile Include="Installer.cs">
|
<Compile Include="Installer.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Operations\ServiceEventCleanupOperation.cs" />
|
||||||
|
<Compile Include="Operations\SessionActivationOperation.cs" />
|
||||||
|
<Compile Include="Operations\SessionInitializationOperation.cs" />
|
||||||
|
<Compile Include="Operations\SessionOperation.cs" />
|
||||||
<Compile Include="Service.cs">
|
<Compile Include="Service.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -92,8 +96,6 @@
|
||||||
<Name>SafeExamBrowser.Logging</Name>
|
<Name>SafeExamBrowser.Logging</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup />
|
||||||
<Folder Include="Operations\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
|
@ -6,23 +6,27 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using SafeExamBrowser.Contracts.Communication.Events;
|
||||||
using SafeExamBrowser.Contracts.Communication.Hosts;
|
using SafeExamBrowser.Contracts.Communication.Hosts;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Contracts.Service;
|
using SafeExamBrowser.Contracts.Service;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Service
|
namespace SafeExamBrowser.Service
|
||||||
{
|
{
|
||||||
internal class ServiceController : IServiceController
|
internal class ServiceController : IServiceController
|
||||||
{
|
{
|
||||||
|
private readonly ILogger logger;
|
||||||
private IOperationSequence bootstrapSequence;
|
private IOperationSequence bootstrapSequence;
|
||||||
private IRepeatableOperationSequence sessionSequence;
|
private IOperationSequence sessionSequence;
|
||||||
private IServiceHost serviceHost;
|
private IServiceHost serviceHost;
|
||||||
private SessionContext sessionContext;
|
private SessionContext sessionContext;
|
||||||
|
|
||||||
private ServiceConfiguration Session
|
private ServiceConfiguration Session
|
||||||
{
|
{
|
||||||
get { return sessionContext.Current; }
|
get { return sessionContext.Configuration; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SessionIsRunning
|
private bool SessionIsRunning
|
||||||
|
@ -31,11 +35,13 @@ namespace SafeExamBrowser.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceController(
|
public ServiceController(
|
||||||
|
ILogger logger,
|
||||||
IOperationSequence bootstrapSequence,
|
IOperationSequence bootstrapSequence,
|
||||||
IRepeatableOperationSequence sessionSequence,
|
IOperationSequence sessionSequence,
|
||||||
IServiceHost serviceHost,
|
IServiceHost serviceHost,
|
||||||
SessionContext sessionContext)
|
SessionContext sessionContext)
|
||||||
{
|
{
|
||||||
|
this.logger = logger;
|
||||||
this.bootstrapSequence = bootstrapSequence;
|
this.bootstrapSequence = bootstrapSequence;
|
||||||
this.sessionSequence = sessionSequence;
|
this.sessionSequence = sessionSequence;
|
||||||
this.serviceHost = serviceHost;
|
this.serviceHost = serviceHost;
|
||||||
|
@ -44,22 +50,130 @@ namespace SafeExamBrowser.Service
|
||||||
|
|
||||||
public bool TryStart()
|
public bool TryStart()
|
||||||
{
|
{
|
||||||
|
logger.Info("Initiating startup procedure...");
|
||||||
|
|
||||||
var result = bootstrapSequence.TryPerform();
|
var result = bootstrapSequence.TryPerform();
|
||||||
var success = result == OperationResult.Success;
|
var success = result == OperationResult.Success;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
RegisterEvents();
|
||||||
|
|
||||||
|
logger.Info("Service successfully initialized.");
|
||||||
|
logger.Log(string.Empty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Info("Service startup aborted!");
|
||||||
|
logger.Log(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Terminate()
|
public void Terminate()
|
||||||
{
|
{
|
||||||
var result = default(OperationResult);
|
DeregisterEvents();
|
||||||
|
|
||||||
if (SessionIsRunning)
|
if (SessionIsRunning)
|
||||||
{
|
{
|
||||||
result = sessionSequence.TryRevert();
|
StopSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = bootstrapSequence.TryRevert();
|
logger.Log(string.Empty);
|
||||||
|
logger.Info("Initiating termination procedure...");
|
||||||
|
|
||||||
|
var result = bootstrapSequence.TryRevert();
|
||||||
|
var success = result == OperationResult.Success;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Info("Service successfully terminated.");
|
||||||
|
logger.Log(string.Empty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Info("Service termination failed!");
|
||||||
|
logger.Log(string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartSession()
|
||||||
|
{
|
||||||
|
logger.Info(AppendDivider("Session Start Procedure"));
|
||||||
|
|
||||||
|
var result = sessionSequence.TryPerform();
|
||||||
|
|
||||||
|
if (result == OperationResult.Success)
|
||||||
|
{
|
||||||
|
logger.Info(AppendDivider("Session Running"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Info(AppendDivider("Session Start Failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopSession()
|
||||||
|
{
|
||||||
|
logger.Info(AppendDivider("Session Stop Procedure"));
|
||||||
|
|
||||||
|
var result = sessionSequence.TryRevert();
|
||||||
|
|
||||||
|
if (result == OperationResult.Success)
|
||||||
|
{
|
||||||
|
logger.Info(AppendDivider("Session Terminated"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Info(AppendDivider("Session Stop Failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterEvents()
|
||||||
|
{
|
||||||
|
serviceHost.SessionStartRequested += ServiceHost_SessionStartRequested;
|
||||||
|
serviceHost.SessionStopRequested += ServiceHost_SessionStopRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeregisterEvents()
|
||||||
|
{
|
||||||
|
serviceHost.SessionStartRequested -= ServiceHost_SessionStartRequested;
|
||||||
|
serviceHost.SessionStopRequested -= ServiceHost_SessionStopRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServiceHost_SessionStartRequested(SessionStartEventArgs args)
|
||||||
|
{
|
||||||
|
if (!SessionIsRunning)
|
||||||
|
{
|
||||||
|
sessionContext.Configuration = args.Configuration;
|
||||||
|
|
||||||
|
StartSession();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Received session start request, even though a session is already running!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServiceHost_SessionStopRequested(SessionStopEventArgs args)
|
||||||
|
{
|
||||||
|
if (SessionIsRunning)
|
||||||
|
{
|
||||||
|
StopSession();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Received session stop request, even though no session is currently running!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string AppendDivider(string message)
|
||||||
|
{
|
||||||
|
var dashesLeft = new String('-', 48 - message.Length / 2 - message.Length % 2);
|
||||||
|
var dashesRight = new String('-', 48 - message.Length / 2);
|
||||||
|
|
||||||
|
return $"### {dashesLeft} {message} {dashesRight} ###";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Service
|
namespace SafeExamBrowser.Service
|
||||||
|
@ -18,11 +19,11 @@ namespace SafeExamBrowser.Service
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The configuration of the currently active session.
|
/// The configuration of the currently active session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal ServiceConfiguration Current { get; set; }
|
internal ServiceConfiguration Configuration { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The configuration of the next session to be activated.
|
/// The global inter-process event used for status synchronization with the runtime component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal ServiceConfiguration Next { get; set; }
|
internal EventWaitHandle EventWaitHandle { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,15 @@ test_script:
|
||||||
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.I18n.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.I18n.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.I18n.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.I18n.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
||||||
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.Logging.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.Logging.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.Logging.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.Logging.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
||||||
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.Runtime.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.Runtime.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.Runtime.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.Runtime.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
||||||
|
- .\packages\OpenCover.4.7.922\tools\OpenCover.Console.exe -register:user -target:"vstest.console.exe" -targetargs:"/logger:Appveyor .\SafeExamBrowser.Service.UnitTests\bin\%PLATFORM%\%CONFIGURATION%\SafeExamBrowser.Service.UnitTests.dll" -filter:"+[*]* -[*.UnitTests]* -[*Moq*]*" -mergebyhash -mergeoutput -output:"coverage.xml"
|
||||||
after_test:
|
after_test:
|
||||||
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
|
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
|
||||||
- pip install codecov
|
- pip install codecov
|
||||||
- codecov -f "coverage.xml"
|
- codecov -f "coverage.xml"
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: SafeExamBrowser.Runtime\bin\$(platform)\$(configuration)
|
- path: SafeExamBrowser.Runtime\bin\$(platform)\$(configuration)
|
||||||
name: '$(appveyor_build_version)_Base_Application'
|
name: '$(appveyor_build_version)_Application'
|
||||||
type: zip
|
type: zip
|
||||||
- path: SafeExamBrowser.Service\bin\$(platform)\$(configuration)
|
- path: SafeExamBrowser.Service\bin\$(platform)\$(configuration)
|
||||||
name: '$(appveyor_build_version)_Base_Service'
|
name: '$(appveyor_build_version)_Service'
|
||||||
type: zip
|
type: zip
|
||||||
|
|
Loading…
Reference in a new issue