Restore SEBPatch
This commit is contained in:
6
SafeExamBrowser.Service/App.config
Normal file
6
SafeExamBrowser.Service/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||
</startup>
|
||||
</configuration>
|
79
SafeExamBrowser.Service/Communication/ServiceHost.cs
Normal file
79
SafeExamBrowser.Service/Communication/ServiceHost.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Communication.Hosts;
|
||||
using SafeExamBrowser.Communication.Contracts;
|
||||
using SafeExamBrowser.Communication.Contracts.Data;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Service.Communication
|
||||
{
|
||||
internal class ServiceHost : BaseHost, IServiceHost
|
||||
{
|
||||
private bool allowConnection;
|
||||
|
||||
public event CommunicationEventHandler<SessionStartEventArgs> SessionStartRequested;
|
||||
public event CommunicationEventHandler<SessionStopEventArgs> SessionStopRequested;
|
||||
public event CommunicationEventHandler SystemConfigurationUpdateRequested;
|
||||
|
||||
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)
|
||||
{
|
||||
var allow = allowConnection;
|
||||
|
||||
if (allow)
|
||||
{
|
||||
allowConnection = false;
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
protected override void OnDisconnect(Interlocutor interlocutor)
|
||||
{
|
||||
if (interlocutor == Interlocutor.Runtime)
|
||||
{
|
||||
allowConnection = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Response OnReceive(Message message)
|
||||
{
|
||||
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)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case SimpleMessagePurport.UpdateSystemConfiguration:
|
||||
SystemConfigurationUpdateRequested?.InvokeAsync();
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
}
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
}
|
||||
}
|
126
SafeExamBrowser.Service/CompositionRoot.cs
Normal file
126
SafeExamBrowser.Service/CompositionRoot.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Communication.Hosts;
|
||||
using SafeExamBrowser.Communication.Proxies;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.OperationModel;
|
||||
using SafeExamBrowser.Core.Operations;
|
||||
using SafeExamBrowser.Lockdown;
|
||||
using SafeExamBrowser.Logging;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Service.Communication;
|
||||
using SafeExamBrowser.Service.Operations;
|
||||
using SafeExamBrowser.Settings.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Service
|
||||
{
|
||||
internal class CompositionRoot
|
||||
{
|
||||
private ILogger logger;
|
||||
|
||||
internal ServiceController ServiceController { get; private set; }
|
||||
|
||||
internal void BuildObjectGraph()
|
||||
{
|
||||
const int ONE_SECOND = 1000;
|
||||
const int FIVE_SECONDS = 5000;
|
||||
const int FIFTEEN_SECONDS = 15000;
|
||||
|
||||
var backupFilePath = BuildBackupFilePath();
|
||||
|
||||
InitializeLogging();
|
||||
|
||||
var featureBackup = new FeatureConfigurationBackup(backupFilePath, new ModuleLogger(logger, nameof(FeatureConfigurationBackup)));
|
||||
var featureFactory = new FeatureConfigurationFactory(new ModuleLogger(logger, nameof(FeatureConfigurationFactory)));
|
||||
var featureMonitor = new FeatureConfigurationMonitor(new ModuleLogger(logger, nameof(FeatureConfigurationMonitor)), ONE_SECOND);
|
||||
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ProxyFactory)));
|
||||
var serviceHost = new ServiceHost(AppConfig.SERVICE_ADDRESS, new HostObjectFactory(), new ModuleLogger(logger, nameof(ServiceHost)), FIFTEEN_SECONDS);
|
||||
var sessionContext = new SessionContext();
|
||||
var systemConfigurationUpdate = new SystemConfigurationUpdate(new ModuleLogger(logger, nameof(SystemConfigurationUpdate)));
|
||||
|
||||
var bootstrapOperations = new Queue<IOperation>();
|
||||
var sessionOperations = new Queue<IOperation>();
|
||||
|
||||
sessionContext.AutoRestoreMechanism = new AutoRestoreMechanism(featureBackup, new ModuleLogger(logger, nameof(AutoRestoreMechanism)), systemConfigurationUpdate, FIVE_SECONDS);
|
||||
|
||||
bootstrapOperations.Enqueue(new RestoreOperation(featureBackup, logger, sessionContext));
|
||||
bootstrapOperations.Enqueue(new CommunicationHostOperation(serviceHost, logger));
|
||||
bootstrapOperations.Enqueue(new ServiceEventCleanupOperation(logger, sessionContext));
|
||||
|
||||
sessionOperations.Enqueue(new SessionInitializationOperation(logger, ServiceEventFactory, sessionContext));
|
||||
sessionOperations.Enqueue(new LockdownOperation(featureBackup, featureFactory, featureMonitor, logger, sessionContext));
|
||||
sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));
|
||||
|
||||
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
|
||||
var sessionSequence = new OperationSequence(logger, sessionOperations);
|
||||
|
||||
ServiceController = new ServiceController(logger, LogWriterFactory, bootstrapSequence, sessionSequence, serviceHost, sessionContext, systemConfigurationUpdate);
|
||||
}
|
||||
|
||||
private string BuildBackupFilePath()
|
||||
{
|
||||
var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser));
|
||||
var filePath = Path.Combine(appDataFolder, AppConfig.BACKUP_FILE_NAME);
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
internal void LogStartupInformation()
|
||||
{
|
||||
logger.Log($"# Service started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
logger.Log(string.Empty);
|
||||
}
|
||||
|
||||
internal void LogShutdownInformation()
|
||||
{
|
||||
logger?.Log($"# Service terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
}
|
||||
|
||||
private void InitializeLogging()
|
||||
{
|
||||
var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser));
|
||||
var logFolder = Path.Combine(appDataFolder, "Logs");
|
||||
var logFilePrefix = DateTime.Now.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
|
||||
var logFilePath = Path.Combine(logFolder, $"{logFilePrefix}_Service.log");
|
||||
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), logFilePath);
|
||||
|
||||
logger = new Logger();
|
||||
logger.LogLevel = LogLevel.Debug;
|
||||
logger.Subscribe(logFileWriter);
|
||||
logFileWriter.Initialize();
|
||||
}
|
||||
|
||||
private ILogObserver LogWriterFactory(string filePath)
|
||||
{
|
||||
var writer = new LogFileWriter(new DefaultLogFormatter(), filePath);
|
||||
|
||||
writer.Initialize();
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
private EventWaitHandle ServiceEventFactory(string eventName)
|
||||
{
|
||||
var securityIdentifier = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
|
||||
var accessRule = new EventWaitHandleAccessRule(securityIdentifier, EventWaitHandleRights.Synchronize, AccessControlType.Allow);
|
||||
var security = new EventWaitHandleSecurity();
|
||||
|
||||
security.AddAccessRule(accessRule);
|
||||
|
||||
return new EventWaitHandle(false, EventResetMode.AutoReset, eventName, out _, security);
|
||||
}
|
||||
}
|
||||
}
|
35
SafeExamBrowser.Service/Installer.cs
Normal file
35
SafeExamBrowser.Service/Installer.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.ComponentModel;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace SafeExamBrowser.Service
|
||||
{
|
||||
[RunInstaller(true)]
|
||||
public class Installer : System.Configuration.Install.Installer
|
||||
{
|
||||
private ServiceProcessInstaller process;
|
||||
private ServiceInstaller service;
|
||||
|
||||
public Installer()
|
||||
{
|
||||
process = new ServiceProcessInstaller();
|
||||
process.Account = ServiceAccount.LocalSystem;
|
||||
|
||||
service = new ServiceInstaller();
|
||||
service.Description = "Performs operations which require elevated privileges.";
|
||||
service.DisplayName = "Safe Exam Browser Service";
|
||||
service.ServiceName = nameof(SafeExamBrowser);
|
||||
service.StartType = ServiceStartMode.Automatic;
|
||||
|
||||
Installers.Add(process);
|
||||
Installers.Add(service);
|
||||
}
|
||||
}
|
||||
}
|
166
SafeExamBrowser.Service/Operations/LockdownOperation.cs
Normal file
166
SafeExamBrowser.Service/Operations/LockdownOperation.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 System.Collections.Generic;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Lockdown.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Service.Operations
|
||||
{
|
||||
internal class LockdownOperation : SessionOperation
|
||||
{
|
||||
private readonly IFeatureConfigurationBackup backup;
|
||||
private readonly IFeatureConfigurationFactory factory;
|
||||
private readonly IFeatureConfigurationMonitor monitor;
|
||||
private readonly ILogger logger;
|
||||
|
||||
private Guid groupId;
|
||||
|
||||
public LockdownOperation(
|
||||
IFeatureConfigurationBackup backup,
|
||||
IFeatureConfigurationFactory factory,
|
||||
IFeatureConfigurationMonitor monitor,
|
||||
ILogger logger,
|
||||
SessionContext sessionContext) : base(sessionContext)
|
||||
{
|
||||
this.backup = backup;
|
||||
this.factory = factory;
|
||||
this.monitor = monitor;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
groupId = Guid.NewGuid();
|
||||
|
||||
var success = true;
|
||||
var sid = Context.Configuration.UserSid;
|
||||
var userName = Context.Configuration.UserName;
|
||||
var configurations = new List<(IFeatureConfiguration, bool)>
|
||||
{
|
||||
(factory.CreateChangePasswordConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisablePasswordChange),
|
||||
(factory.CreateChromeNotificationConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisableChromeNotifications),
|
||||
(factory.CreateEaseOfAccessConfiguration(groupId), Context.Configuration.Settings.Service.DisableEaseOfAccessOptions),
|
||||
(factory.CreateFindPrinterConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisableFindPrinter),
|
||||
(factory.CreateLockWorkstationConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisableUserLock),
|
||||
(factory.CreateMachinePowerOptionsConfiguration(groupId), Context.Configuration.Settings.Service.DisablePowerOptions),
|
||||
(factory.CreateNetworkOptionsConfiguration(groupId), Context.Configuration.Settings.Service.DisableNetworkOptions),
|
||||
(factory.CreateRemoteConnectionConfiguration(groupId), Context.Configuration.Settings.Service.DisableRemoteConnections),
|
||||
(factory.CreateSignoutConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisableSignout),
|
||||
(factory.CreateSwitchUserConfiguration(groupId), Context.Configuration.Settings.Service.DisableUserSwitch),
|
||||
(factory.CreateTaskManagerConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisableTaskManager),
|
||||
(factory.CreateUserPowerOptionsConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisablePowerOptions),
|
||||
(factory.CreateWindowsUpdateConfiguration(groupId), Context.Configuration.Settings.Service.DisableWindowsUpdate)
|
||||
};
|
||||
|
||||
if (Context.Configuration.Settings.Service.SetVmwareConfiguration)
|
||||
{
|
||||
configurations.Add((factory.CreateVmwareOverlayConfiguration(groupId, sid, userName), Context.Configuration.Settings.Service.DisableVmwareOverlay));
|
||||
}
|
||||
|
||||
logger.Info($"Attempting to perform lockdown (feature configuration group: {groupId})...");
|
||||
|
||||
foreach (var (configuration, disable) in configurations)
|
||||
{
|
||||
success &= TrySet(configuration, disable);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
monitor.Start();
|
||||
logger.Info("Lockdown successful.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Lockdown was not successful!");
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info($"Attempting to revert lockdown (feature configuration group: {groupId})...");
|
||||
|
||||
var configurations = backup.GetBy(groupId);
|
||||
var success = true;
|
||||
|
||||
monitor.Reset();
|
||||
|
||||
foreach (var configuration in configurations)
|
||||
{
|
||||
success &= TryRestore(configuration);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Lockdown reversion successful.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Lockdown reversion was not successful!");
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
private bool TryRestore(IFeatureConfiguration configuration)
|
||||
{
|
||||
var success = configuration.Restore();
|
||||
|
||||
if (success)
|
||||
{
|
||||
backup.Delete(configuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to restore {configuration}!");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool TrySet(IFeatureConfiguration configuration, bool disable)
|
||||
{
|
||||
var success = false;
|
||||
var status = FeatureConfigurationStatus.Undefined;
|
||||
|
||||
configuration.Initialize();
|
||||
backup.Save(configuration);
|
||||
|
||||
if (disable)
|
||||
{
|
||||
success = configuration.DisableFeature();
|
||||
status = FeatureConfigurationStatus.Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = configuration.EnableFeature();
|
||||
status = FeatureConfigurationStatus.Enabled;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
monitor.Observe(configuration, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to configure {configuration}!");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
48
SafeExamBrowser.Service/Operations/RestoreOperation.cs
Normal file
48
SafeExamBrowser.Service/Operations/RestoreOperation.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.Lockdown.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Service.Operations
|
||||
{
|
||||
internal class RestoreOperation : IOperation
|
||||
{
|
||||
private readonly IFeatureConfigurationBackup backup;
|
||||
private ILogger logger;
|
||||
private readonly SessionContext sessionContext;
|
||||
|
||||
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public event StatusChangedEventHandler StatusChanged { add { } remove { } }
|
||||
|
||||
public RestoreOperation(IFeatureConfigurationBackup backup, ILogger logger, SessionContext sessionContext)
|
||||
{
|
||||
this.backup = backup;
|
||||
this.logger = logger;
|
||||
this.sessionContext = sessionContext;
|
||||
}
|
||||
|
||||
public OperationResult Perform()
|
||||
{
|
||||
logger.Info("Starting auto-restore mechanism...");
|
||||
sessionContext.AutoRestoreMechanism.Start();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public OperationResult Revert()
|
||||
{
|
||||
logger.Info("Stopping auto-restore mechanism...");
|
||||
sessionContext.AutoRestoreMechanism.Stop();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
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.ServiceEvent != null)
|
||||
{
|
||||
logger.Info("Closing service event...");
|
||||
sessionContext.ServiceEvent.Close();
|
||||
logger.Info("Service event successfully closed.");
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
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.ServiceEvent.Set();
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Successfully informed runtime about new session activation.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to inform runtime about new session activation!");
|
||||
}
|
||||
|
||||
Context.IsRunning = success;
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 System.Threading;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Service.Operations
|
||||
{
|
||||
internal class SessionInitializationOperation : SessionOperation
|
||||
{
|
||||
private ILogger logger;
|
||||
private Func<string, EventWaitHandle> serviceEventFactory;
|
||||
|
||||
public SessionInitializationOperation(ILogger logger, Func<string, EventWaitHandle> serviceEventFactory, SessionContext sessionContext) : base(sessionContext)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.serviceEventFactory = serviceEventFactory;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing new session...");
|
||||
logger.Info($" -> Client-ID: {Context.Configuration.AppConfig.ClientId}");
|
||||
logger.Info($" -> Runtime-ID: {Context.Configuration.AppConfig.RuntimeId}");
|
||||
logger.Info($" -> Session-ID: {Context.Configuration.SessionId}");
|
||||
|
||||
logger.Info("Stopping auto-restore mechanism...");
|
||||
Context.AutoRestoreMechanism.Stop();
|
||||
|
||||
InitializeServiceEvent();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
var success = true;
|
||||
var wasRunning = Context.IsRunning;
|
||||
|
||||
logger.Info("Starting auto-restore mechanism...");
|
||||
Context.AutoRestoreMechanism.Start();
|
||||
|
||||
logger.Info("Clearing session data...");
|
||||
Context.Configuration = null;
|
||||
Context.IsRunning = false;
|
||||
|
||||
if (Context.ServiceEvent != null && wasRunning)
|
||||
{
|
||||
success = Context.ServiceEvent.Set();
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Successfully informed runtime about session termination.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to inform runtime about session termination!");
|
||||
}
|
||||
}
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
private void InitializeServiceEvent()
|
||||
{
|
||||
if (Context.ServiceEvent != null)
|
||||
{
|
||||
logger.Info("Closing service event from previous session...");
|
||||
Context.ServiceEvent.Close();
|
||||
logger.Info("Service event successfully closed.");
|
||||
}
|
||||
|
||||
logger.Info("Attempting to create new service event...");
|
||||
Context.ServiceEvent = serviceEventFactory.Invoke(Context.Configuration.AppConfig.ServiceEventName);
|
||||
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) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.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();
|
||||
}
|
||||
}
|
35
SafeExamBrowser.Service/Properties/AssemblyInfo.cs
Normal file
35
SafeExamBrowser.Service/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SafeExamBrowser.Service")]
|
||||
[assembly: AssemblyDescription("Safe Exam Browser")]
|
||||
[assembly: AssemblyCompany("ETH Zürich")]
|
||||
[assembly: AssemblyProduct("SafeExamBrowser.Service")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: InternalsVisibleTo("SafeExamBrowser.Service.UnitTests")]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("fa3c6692-dfed-4afa-bd58-9a3da2753c78")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: AssemblyInformationalVersion("1.0.0.0")]
|
134
SafeExamBrowser.Service/SafeExamBrowser.Service.csproj
Normal file
134
SafeExamBrowser.Service/SafeExamBrowser.Service.csproj
Normal file
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FA3C6692-DFED-4AFA-BD58-9A3DA2753C78}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SafeExamBrowser.Service</RootNamespace>
|
||||
<AssemblyName>SafeExamBrowser.Service</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>SafeExamBrowser.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Communication\ServiceHost.cs" />
|
||||
<Compile Include="CompositionRoot.cs" />
|
||||
<Compile Include="Installer.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Operations\LockdownOperation.cs" />
|
||||
<Compile Include="Operations\RestoreOperation.cs" />
|
||||
<Compile Include="Operations\ServiceEventCleanupOperation.cs" />
|
||||
<Compile Include="Operations\SessionActivationOperation.cs" />
|
||||
<Compile Include="Operations\SessionInitializationOperation.cs" />
|
||||
<Compile Include="Operations\SessionOperation.cs" />
|
||||
<Compile Include="Service.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServiceController.cs" />
|
||||
<Compile Include="SessionContext.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Communication.Contracts\SafeExamBrowser.Communication.Contracts.csproj">
|
||||
<Project>{0cd2c5fe-711a-4c32-afe0-bb804fe8b220}</Project>
|
||||
<Name>SafeExamBrowser.Communication.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Communication\SafeExamBrowser.Communication.csproj">
|
||||
<Project>{c9416a62-0623-4d38-96aa-92516b32f02f}</Project>
|
||||
<Name>SafeExamBrowser.Communication</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj">
|
||||
<Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project>
|
||||
<Name>SafeExamBrowser.Configuration.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Core.Contracts\SafeExamBrowser.Core.Contracts.csproj">
|
||||
<Project>{fe0e1224-b447-4b14-81e7-ed7d84822aa0}</Project>
|
||||
<Name>SafeExamBrowser.Core.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Core\SafeExamBrowser.Core.csproj">
|
||||
<Project>{3d6fdbb6-a4af-4626-bb2b-bf329d44f9cc}</Project>
|
||||
<Name>SafeExamBrowser.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Lockdown.Contracts\SafeExamBrowser.Lockdown.Contracts.csproj">
|
||||
<Project>{3368b17d-6060-4482-9983-aa800d74041d}</Project>
|
||||
<Name>SafeExamBrowser.Lockdown.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Lockdown\SafeExamBrowser.Lockdown.csproj">
|
||||
<Project>{386b6042-3e12-4753-9fc6-c88ea4f97030}</Project>
|
||||
<Name>SafeExamBrowser.Lockdown</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
|
||||
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
|
||||
<Name>SafeExamBrowser.Logging.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging\SafeExamBrowser.Logging.csproj">
|
||||
<Project>{e107026c-2011-4552-a7d8-3a0d37881df6}</Project>
|
||||
<Name>SafeExamBrowser.Logging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
|
||||
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
|
||||
<Name>SafeExamBrowser.Settings</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="SafeExamBrowser.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
BIN
SafeExamBrowser.Service/SafeExamBrowser.ico
Normal file
BIN
SafeExamBrowser.Service/SafeExamBrowser.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 361 KiB |
49
SafeExamBrowser.Service/Service.cs
Normal file
49
SafeExamBrowser.Service/Service.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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 System.ServiceProcess;
|
||||
|
||||
namespace SafeExamBrowser.Service
|
||||
{
|
||||
public class Service : ServiceBase
|
||||
{
|
||||
private CompositionRoot instances;
|
||||
|
||||
public Service()
|
||||
{
|
||||
CanPauseAndContinue = false;
|
||||
ServiceName = nameof(SafeExamBrowser);
|
||||
}
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
Run(new Service());
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
instances = new CompositionRoot();
|
||||
instances.BuildObjectGraph();
|
||||
instances.LogStartupInformation();
|
||||
|
||||
var success = instances.ServiceController.TryStart();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
instances?.ServiceController?.Terminate();
|
||||
instances?.LogShutdownInformation();
|
||||
}
|
||||
}
|
||||
}
|
227
SafeExamBrowser.Service/ServiceController.cs
Normal file
227
SafeExamBrowser.Service/ServiceController.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Communication.Contracts.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Lockdown.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Service
|
||||
{
|
||||
internal class ServiceController
|
||||
{
|
||||
private ILogger logger;
|
||||
private Func<string, ILogObserver> logWriterFactory;
|
||||
private IOperationSequence bootstrapSequence;
|
||||
private IOperationSequence sessionSequence;
|
||||
private IServiceHost serviceHost;
|
||||
private SessionContext sessionContext;
|
||||
private ISystemConfigurationUpdate systemConfigurationUpdate;
|
||||
private ILogObserver sessionWriter;
|
||||
|
||||
private ServiceConfiguration Session
|
||||
{
|
||||
get { return sessionContext.Configuration; }
|
||||
}
|
||||
|
||||
private bool SessionIsRunning
|
||||
{
|
||||
get { return sessionContext.IsRunning; }
|
||||
}
|
||||
|
||||
internal ServiceController(
|
||||
ILogger logger,
|
||||
Func<string, ILogObserver> logWriterFactory,
|
||||
IOperationSequence bootstrapSequence,
|
||||
IOperationSequence sessionSequence,
|
||||
IServiceHost serviceHost,
|
||||
SessionContext sessionContext,
|
||||
ISystemConfigurationUpdate systemConfigurationUpdate)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.logWriterFactory = logWriterFactory;
|
||||
this.bootstrapSequence = bootstrapSequence;
|
||||
this.sessionSequence = sessionSequence;
|
||||
this.serviceHost = serviceHost;
|
||||
this.sessionContext = sessionContext;
|
||||
this.systemConfigurationUpdate = systemConfigurationUpdate;
|
||||
}
|
||||
|
||||
internal bool TryStart()
|
||||
{
|
||||
logger.Info("Initiating startup procedure...");
|
||||
|
||||
var result = bootstrapSequence.TryPerform();
|
||||
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;
|
||||
}
|
||||
|
||||
internal void Terminate()
|
||||
{
|
||||
DeregisterEvents();
|
||||
|
||||
if (SessionIsRunning)
|
||||
{
|
||||
StopSession();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
InitializeSessionLogging();
|
||||
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"));
|
||||
}
|
||||
|
||||
FinalizeSessionLogging();
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
serviceHost.SessionStartRequested += ServiceHost_SessionStartRequested;
|
||||
serviceHost.SessionStopRequested += ServiceHost_SessionStopRequested;
|
||||
serviceHost.SystemConfigurationUpdateRequested += ServiceHost_SystemConfigurationUpdateRequested;
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
serviceHost.SessionStartRequested -= ServiceHost_SessionStartRequested;
|
||||
serviceHost.SessionStopRequested -= ServiceHost_SessionStopRequested;
|
||||
serviceHost.SystemConfigurationUpdateRequested -= ServiceHost_SystemConfigurationUpdateRequested;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (Session.SessionId == args.SessionId)
|
||||
{
|
||||
StopSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Received session stop request with wrong session ID!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Received session stop request, even though no session is currently running!");
|
||||
}
|
||||
}
|
||||
|
||||
private void ServiceHost_SystemConfigurationUpdateRequested()
|
||||
{
|
||||
logger.Info("Received request to initiate system configuration update.");
|
||||
systemConfigurationUpdate.ExecuteAsync();
|
||||
}
|
||||
|
||||
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} ###";
|
||||
}
|
||||
|
||||
private void InitializeSessionLogging()
|
||||
{
|
||||
if (Session?.AppConfig?.ServiceLogFilePath != null)
|
||||
{
|
||||
sessionWriter = logWriterFactory.Invoke(Session.AppConfig.ServiceLogFilePath);
|
||||
logger.Subscribe(sessionWriter);
|
||||
logger.LogLevel = Session.Settings.LogLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Could not initialize session writer due to missing configuration data!");
|
||||
}
|
||||
}
|
||||
|
||||
private void FinalizeSessionLogging()
|
||||
{
|
||||
if (sessionWriter != null)
|
||||
{
|
||||
logger.Unsubscribe(sessionWriter);
|
||||
sessionWriter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
SafeExamBrowser.Service/SessionContext.cs
Normal file
40
SafeExamBrowser.Service/SessionContext.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Threading;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Lockdown.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Service
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds all configuration and runtime data required for the session handling.
|
||||
/// </summary>
|
||||
internal class SessionContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The mechanism trying to restore all changes after an application or system failure.
|
||||
/// </summary>
|
||||
internal IAutoRestoreMechanism AutoRestoreMechanism { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The configuration of the currently active session.
|
||||
/// </summary>
|
||||
internal ServiceConfiguration Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a session is currently running.
|
||||
/// </summary>
|
||||
internal bool IsRunning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The global inter-process event used for status synchronization with the runtime component.
|
||||
/// </summary>
|
||||
internal EventWaitHandle ServiceEvent { get; set; }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user