diff --git a/NAPS2.Sdk.Tests/Config/ConfigScopeTests.cs b/NAPS2.Sdk.Tests/Config/ConfigScopeTests.cs index 9bf55c9a3..a3e9fab41 100644 --- a/NAPS2.Sdk.Tests/Config/ConfigScopeTests.cs +++ b/NAPS2.Sdk.Tests/Config/ConfigScopeTests.cs @@ -33,7 +33,7 @@ namespace NAPS2.Sdk.Tests.Config public void InternalDefaultsNotNullProps() { var config = InternalDefaults.GetCommonConfig(); - AssertPropNullOrNotNull(config, false); + AssertPropNullOrNotNull(config, false, ""); } [Fact] @@ -42,19 +42,19 @@ namespace NAPS2.Sdk.Tests.Config var config = new CommonConfig(); Assert.NotNull(config.Version); config.Version = null; - AssertPropNullOrNotNull(config, true); + AssertPropNullOrNotNull(config, true, ""); } - private static void AssertPropNullOrNotNull(object config, bool shouldBeNull) + private static void AssertPropNullOrNotNull(object config, bool shouldBeNull, string path) { - Assert.NotNull(config); + Assert.True(config != null, path); foreach (var prop in config.GetType().GetProperties()) { var value = prop.GetValue(config); if (prop.CustomAttributes.Any(x => typeof(ChildAttribute).IsAssignableFrom(x.AttributeType))) { // Child, so recurse - AssertPropNullOrNotNull(value, shouldBeNull); + AssertPropNullOrNotNull(value, shouldBeNull, $"{path}{prop.Name}."); } else { diff --git a/NAPS2.Sdk/Config/CommonConfig.cs b/NAPS2.Sdk/Config/CommonConfig.cs index 34ccf6213..b645dea86 100644 --- a/NAPS2.Sdk/Config/CommonConfig.cs +++ b/NAPS2.Sdk/Config/CommonConfig.cs @@ -25,6 +25,7 @@ namespace NAPS2.Config EmailSetup = new EmailSetup(); BatchSettings = new BatchSettings(); KeyboardShortcuts = new KeyboardShortcuts(); + SslSetup = new SslSetup(); } [Common] @@ -176,6 +177,10 @@ namespace NAPS2.Config [Child] [Common] public KeyboardShortcuts KeyboardShortcuts { get; set; } + + [Child] + [Common] + public SslSetup SslSetup { get; set; } [Common] public ScanProfile DefaultProfileSettings { get; set; } diff --git a/NAPS2.Sdk/Config/InternalDefaults.cs b/NAPS2.Sdk/Config/InternalDefaults.cs index 20a300529..d0d5decb7 100644 --- a/NAPS2.Sdk/Config/InternalDefaults.cs +++ b/NAPS2.Sdk/Config/InternalDefaults.cs @@ -16,7 +16,6 @@ namespace NAPS2.Config { public static class InternalDefaults { - // TODO: Test that no properties are null public static CommonConfig GetCommonConfig() => new CommonConfig { @@ -184,6 +183,11 @@ namespace NAPS2.Config ZoomIn = "Ctrl+Oemplus", ZoomOut = "Ctrl+OemMinus" }, + SslSetup = new SslSetup + { + WorkerCert = "", + WorkerPrivateKey = "" + }, DefaultProfileSettings = new ScanProfile { Version = ScanProfile.CURRENT_VERSION } }; } diff --git a/NAPS2.Sdk/Config/SslSetup.cs b/NAPS2.Sdk/Config/SslSetup.cs new file mode 100644 index 000000000..881f90de0 --- /dev/null +++ b/NAPS2.Sdk/Config/SslSetup.cs @@ -0,0 +1,11 @@ +using NAPS2.Serialization; + +namespace NAPS2.Config +{ + public class SslSetup + { + public SecureString WorkerCert { get; set; } + + public SecureString WorkerPrivateKey { get; set; } + } +} \ No newline at end of file diff --git a/NAPS2.Sdk/NAPS2.Sdk.csproj b/NAPS2.Sdk/NAPS2.Sdk.csproj index d43b4a94e..df273bcf6 100644 --- a/NAPS2.Sdk/NAPS2.Sdk.csproj +++ b/NAPS2.Sdk/NAPS2.Sdk.csproj @@ -137,6 +137,7 @@ True ClientCreds.resx + diff --git a/NAPS2.Sdk/Remoting/Worker/WorkerFactory.cs b/NAPS2.Sdk/Remoting/Worker/WorkerFactory.cs index d3adb08bd..5e8cb7318 100644 --- a/NAPS2.Sdk/Remoting/Worker/WorkerFactory.cs +++ b/NAPS2.Sdk/Remoting/Worker/WorkerFactory.cs @@ -5,6 +5,7 @@ using System.IO; using System.Reflection; using System.Text; using System.Threading.Tasks; +using NAPS2.Config; using NAPS2.Images.Storage; using NAPS2.Platform; using NAPS2.Util; @@ -20,11 +21,12 @@ namespace NAPS2.Remoting.Worker public static IWorkerFactory Default { - get => _default ?? (_default = new WorkerFactory(ImageContext.Default)); + get => _default ??= new WorkerFactory(ImageContext.Default); set => _default = value ?? throw new ArgumentNullException(nameof(value)); } public const string WORKER_EXE_NAME = "NAPS2.Worker.exe"; + public static readonly string[] SearchDirs = { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @@ -32,6 +34,8 @@ namespace NAPS2.Remoting.Worker }; private readonly ImageContext imageContext; + private readonly ConfigProvider configProvider; + private readonly ConfigScopes configScopes; private string workerExePath; private BlockingCollection workerQueue; @@ -41,6 +45,13 @@ namespace NAPS2.Remoting.Worker this.imageContext = imageContext; } + public WorkerFactory(ImageContext imageContext, ConfigProvider configProvider, ConfigScopes configScopes) + { + this.imageContext = imageContext; + this.configProvider = configProvider; + this.configScopes = configScopes; + } + private string WorkerExePath { get @@ -90,7 +101,7 @@ namespace NAPS2.Remoting.Worker } } - var (cert, privateKey) = SslHelper.GenerateRootCertificate(); + var (cert, privateKey) = GetOrCreateCertAndPrivateKey(); WriteEncodedString(proc.StandardInput, cert); WriteEncodedString(proc.StandardInput, privateKey); var portStr = proc.StandardOutput.ReadLine(); @@ -103,6 +114,19 @@ namespace NAPS2.Remoting.Worker return (proc, port, cert, privateKey); } + private (string cert, string privateKey) GetOrCreateCertAndPrivateKey() + { + string cert = configProvider?.Get(c => c.SslSetup.WorkerCert); + string privateKey = configProvider?.Get(c => c.SslSetup.WorkerPrivateKey); + if (string.IsNullOrEmpty(cert) || string.IsNullOrEmpty(privateKey)) + { + (cert, privateKey) = SslHelper.GenerateRootCertificate(); + configScopes?.User.Set(c => c.SslSetup.WorkerCert = cert); + configScopes?.User.Set(c => c.SslSetup.WorkerPrivateKey = privateKey); + } + return (cert, privateKey); + } + private void WriteEncodedString(StreamWriter streamWriter, string value) { streamWriter.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(value)));