SEBWIN-322: Implemented service configuration infrastructure and windows update service deactivation.
This commit is contained in:
parent
0d793dd326
commit
9dece7e4ae
9 changed files with 388 additions and 50 deletions
|
@ -145,7 +145,7 @@ namespace SafeExamBrowser.Lockdown.UnitTests
|
||||||
Thread.Sleep(25);
|
Thread.Sleep(25);
|
||||||
sut.Stop();
|
sut.Stop();
|
||||||
|
|
||||||
backup.Verify(b => b.GetAllConfigurations(), Times.Exactly(counter));
|
backup.Verify(b => b.GetAllConfigurations(), Times.Between(counter, counter + 1, Range.Inclusive));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
using System;
|
using System;
|
||||||
using SafeExamBrowser.Contracts.Lockdown;
|
using SafeExamBrowser.Contracts.Lockdown;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Lockdown.FeatureConfigurations;
|
|
||||||
using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive;
|
using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive;
|
||||||
using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive;
|
using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive;
|
||||||
|
using SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Lockdown
|
namespace SafeExamBrowser.Lockdown
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* 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 System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using SafeExamBrowser.Contracts.Lockdown;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
internal abstract class ServiceConfiguration : FeatureConfiguration
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan FIVE_SECONDS = TimeSpan.FromSeconds(5);
|
||||||
|
private IList<ServiceDataItem> originalItems;
|
||||||
|
|
||||||
|
protected abstract IEnumerable<ServiceConfigurationItem> Items { get; }
|
||||||
|
|
||||||
|
public ServiceConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
|
||||||
|
{
|
||||||
|
originalItems = new List<ServiceDataItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool DisableFeature()
|
||||||
|
{
|
||||||
|
var success = true;
|
||||||
|
|
||||||
|
foreach (var item in Items)
|
||||||
|
{
|
||||||
|
success &= TrySet(new ServiceDataItem { Name = item.Name, Status = item.Disabled });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Info("Successfully disabled feature.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Failed to disable feature!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EnableFeature()
|
||||||
|
{
|
||||||
|
var success = true;
|
||||||
|
|
||||||
|
foreach (var item in Items)
|
||||||
|
{
|
||||||
|
success &= TrySet(new ServiceDataItem { Name = item.Name, Status = item.Enabled });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Info("Successfully enabled feature.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Failed to enable feature!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override FeatureConfigurationStatus GetStatus()
|
||||||
|
{
|
||||||
|
var status = FeatureConfigurationStatus.Undefined;
|
||||||
|
|
||||||
|
foreach (var item in Items)
|
||||||
|
{
|
||||||
|
var current = ReadService(item.Name);
|
||||||
|
|
||||||
|
if (current.Status == item.Disabled && status != FeatureConfigurationStatus.Enabled)
|
||||||
|
{
|
||||||
|
status = FeatureConfigurationStatus.Disabled;
|
||||||
|
}
|
||||||
|
else if (current.Status == item.Enabled && status != FeatureConfigurationStatus.Disabled)
|
||||||
|
{
|
||||||
|
status = FeatureConfigurationStatus.Enabled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = FeatureConfigurationStatus.Undefined;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
foreach (var item in Items)
|
||||||
|
{
|
||||||
|
var original = ReadService(item.Name);
|
||||||
|
|
||||||
|
if (original.Status != ServiceStatus.NotAvailable)
|
||||||
|
{
|
||||||
|
originalItems.Add(original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Restore()
|
||||||
|
{
|
||||||
|
foreach (var item in new List<ServiceDataItem>(originalItems))
|
||||||
|
{
|
||||||
|
if (TrySet(item))
|
||||||
|
{
|
||||||
|
originalItems.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = !originalItems.Any();
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Info("Successfully restored feature.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Failed to restore feature!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceDataItem ReadService(string name)
|
||||||
|
{
|
||||||
|
var item = new ServiceDataItem { Name = name, Status = ServiceStatus.NotAvailable };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var service = new ServiceController(name))
|
||||||
|
{
|
||||||
|
item.Status = ToStatus(service.Status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to retrieve status of service '{name}'!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TrySet(ServiceDataItem item)
|
||||||
|
{
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (IsAvailable(item))
|
||||||
|
{
|
||||||
|
using (var service = new ServiceController(item.Name))
|
||||||
|
{
|
||||||
|
if (item.Status == ServiceStatus.Running)
|
||||||
|
{
|
||||||
|
success = TryStart(service);
|
||||||
|
}
|
||||||
|
else if (item.Status == ServiceStatus.Stopped)
|
||||||
|
{
|
||||||
|
success = TryStop(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.Debug($"Successfully set service {item}.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn($"Could not set service {item}! Current status: {service.Status}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn($"Cannot set service {item} as it does not exist!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to set service {item}!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryStart(ServiceController service)
|
||||||
|
{
|
||||||
|
var success = true;
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.PausePending)
|
||||||
|
{
|
||||||
|
service.WaitForStatus(ServiceControllerStatus.Paused, FIVE_SECONDS);
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.Paused)
|
||||||
|
{
|
||||||
|
service.Continue();
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.Stopped)
|
||||||
|
{
|
||||||
|
service.Start();
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.StartPending || service.Status == ServiceControllerStatus.ContinuePending)
|
||||||
|
{
|
||||||
|
service.WaitForStatus(ServiceControllerStatus.Running, FIVE_SECONDS);
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.Running)
|
||||||
|
{
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryStop(ServiceController service)
|
||||||
|
{
|
||||||
|
var success = false;
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.Paused)
|
||||||
|
{
|
||||||
|
service.Continue();
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.StartPending || service.Status == ServiceControllerStatus.ContinuePending)
|
||||||
|
{
|
||||||
|
service.WaitForStatus(ServiceControllerStatus.Running, FIVE_SECONDS);
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.Running)
|
||||||
|
{
|
||||||
|
service.Stop();
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.StopPending)
|
||||||
|
{
|
||||||
|
service.WaitForStatus(ServiceControllerStatus.Stopped, FIVE_SECONDS);
|
||||||
|
service.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service.Status == ServiceControllerStatus.Stopped)
|
||||||
|
{
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAvailable(ServiceDataItem item)
|
||||||
|
{
|
||||||
|
return ServiceController.GetServices().Any(s => s.ServiceName == item.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServiceStatus ToStatus(ServiceControllerStatus status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case ServiceControllerStatus.ContinuePending:
|
||||||
|
case ServiceControllerStatus.Running:
|
||||||
|
case ServiceControllerStatus.StartPending:
|
||||||
|
return ServiceStatus.Running;
|
||||||
|
default:
|
||||||
|
return ServiceStatus.Stopped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
|
||||||
|
{
|
||||||
|
internal class ServiceConfigurationItem
|
||||||
|
{
|
||||||
|
internal ServiceStatus Disabled { get; }
|
||||||
|
internal ServiceStatus Enabled { get; }
|
||||||
|
internal string Name { get; }
|
||||||
|
|
||||||
|
internal ServiceConfigurationItem(string name, ServiceStatus disabled, ServiceStatus enabled)
|
||||||
|
{
|
||||||
|
Disabled = disabled;
|
||||||
|
Enabled = enabled;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.Lockdown.FeatureConfigurations.ServiceConfigurations
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
internal class ServiceDataItem
|
||||||
|
{
|
||||||
|
internal string Name { get; set; }
|
||||||
|
internal ServiceStatus Status { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $@"'{Name}' => '{(Status == ServiceStatus.Running ? ServiceStatus.Running.ToString().ToLower() : ServiceStatus.Stopped.ToString().ToLower())}'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
|
||||||
|
{
|
||||||
|
internal enum ServiceStatus
|
||||||
|
{
|
||||||
|
NotAvailable,
|
||||||
|
Running,
|
||||||
|
Stopped
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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 System.Collections.Generic;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
internal class WindowsUpdateConfiguration : ServiceConfiguration
|
||||||
|
{
|
||||||
|
protected override IEnumerable<ServiceConfigurationItem> Items => new []
|
||||||
|
{
|
||||||
|
new ServiceConfigurationItem("wuauserv", ServiceStatus.Stopped, ServiceStatus.Running)
|
||||||
|
};
|
||||||
|
|
||||||
|
public WindowsUpdateConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +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 System;
|
|
||||||
using SafeExamBrowser.Contracts.Lockdown;
|
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Lockdown.FeatureConfigurations
|
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
internal class WindowsUpdateConfiguration : FeatureConfiguration
|
|
||||||
{
|
|
||||||
public WindowsUpdateConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool DisableFeature()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EnableFeature()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override FeatureConfigurationStatus GetStatus()
|
|
||||||
{
|
|
||||||
return FeatureConfigurationStatus.Undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Restore()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -51,6 +51,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.ServiceProcess" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AutoRestoreMechanism.cs" />
|
<Compile Include="AutoRestoreMechanism.cs" />
|
||||||
|
@ -74,7 +75,11 @@
|
||||||
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\LockWorkstationConfiguration.cs" />
|
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\LockWorkstationConfiguration.cs" />
|
||||||
<Compile Include="FeatureConfigurations\RegistryConfigurations\MachineHive\SwitchUserConfiguration.cs" />
|
<Compile Include="FeatureConfigurations\RegistryConfigurations\MachineHive\SwitchUserConfiguration.cs" />
|
||||||
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\VmwareOverlayConfiguration.cs" />
|
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\VmwareOverlayConfiguration.cs" />
|
||||||
<Compile Include="FeatureConfigurations\WindowsUpdateConfiguration.cs" />
|
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceConfiguration.cs" />
|
||||||
|
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceConfigurationItem.cs" />
|
||||||
|
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceDataItem.cs" />
|
||||||
|
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceStatus.cs" />
|
||||||
|
<Compile Include="FeatureConfigurations\ServiceConfigurations\WindowsUpdateConfiguration.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="SystemConfigurationUpdate.cs" />
|
<Compile Include="SystemConfigurationUpdate.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
Loading…
Add table
Reference in a new issue