diff options
-rw-r--r-- | src/main.lib/Plugins/Base/OptionsFactories/ValidationPluginOptionsFactory.cs | 1 | ||||
-rw-r--r-- | src/main.lib/Plugins/Resolvers/InteractiveResolver.cs | 75 | ||||
-rw-r--r-- | src/main.lib/Plugins/Resolvers/UnattendedResolver.cs | 273 | ||||
-rw-r--r-- | src/main.lib/Services/ArgumentsParser.cs | 4 | ||||
-rw-r--r-- | src/main.lib/Services/AutofacBuilder.cs | 2 | ||||
-rw-r--r-- | src/main.lib/Services/Interfaces/IPluginService.cs | 20 | ||||
-rw-r--r-- | src/main.lib/Services/PluginService.cs | 121 | ||||
-rw-r--r-- | src/main.lib/Services/SettingsService.cs | 2 | ||||
-rw-r--r-- | src/main.test/Mock/Clients/IISClient.cs | 2 | ||||
-rw-r--r-- | src/main.test/Tests/BindingTests/Bindings.cs | 20 | ||||
-rw-r--r-- | src/main.test/Tests/InstallationPluginTests/MultipleInstallerTests.cs | 23 | ||||
-rw-r--r-- | src/main/settings.json | 2 |
12 files changed, 248 insertions, 297 deletions
diff --git a/src/main.lib/Plugins/Base/OptionsFactories/ValidationPluginOptionsFactory.cs b/src/main.lib/Plugins/Base/OptionsFactories/ValidationPluginOptionsFactory.cs index 690c89a..131bd7f 100644 --- a/src/main.lib/Plugins/Base/OptionsFactories/ValidationPluginOptionsFactory.cs +++ b/src/main.lib/Plugins/Base/OptionsFactories/ValidationPluginOptionsFactory.cs @@ -16,7 +16,6 @@ namespace PKISharp.WACS.Plugins.Base.Factories { private readonly string _challengeType; string IValidationPluginOptionsFactory.ChallengeType => _challengeType; - public virtual bool Hidden => false; public ValidationPluginOptionsFactory(string challengeType = Constants.Http01ChallengeType) => _challengeType = challengeType; public abstract Task<TOptions?> Aquire(Target target, IInputService inputService, RunLevel runLevel); diff --git a/src/main.lib/Plugins/Resolvers/InteractiveResolver.cs b/src/main.lib/Plugins/Resolvers/InteractiveResolver.cs index c7f98fa..2c682ec 100644 --- a/src/main.lib/Plugins/Resolvers/InteractiveResolver.cs +++ b/src/main.lib/Plugins/Resolvers/InteractiveResolver.cs @@ -1,5 +1,6 @@ using Autofac; using PKISharp.WACS.DomainObjects; +using PKISharp.WACS.Extensions; using PKISharp.WACS.Plugins.Base.Factories.Null; using PKISharp.WACS.Plugins.CsrPlugins; using PKISharp.WACS.Plugins.InstallationPlugins; @@ -39,8 +40,6 @@ namespace PKISharp.WACS.Plugins.Resolvers private async Task<T> GetPlugin<T>( ILifetimeScope scope, - IEnumerable<T> options, - Func<ILifetimeScope, string, string?, T> resolver, Type defaultType, Type defaultTypeFallback, T nullResult, @@ -49,6 +48,8 @@ namespace PKISharp.WACS.Plugins.Resolvers string longDescription, string? defaultParam1 = null, string? defaultParam2 = null, + Func<IEnumerable<T>, IEnumerable<T>>? sort = null, + Func<IEnumerable<T>, IEnumerable<T>>? filter = null, Func<T, (bool, string?)>? unusable = null, Func<T, string>? description = null, bool allowAbort = true) where T : IPluginOptionsFactory @@ -72,10 +73,10 @@ namespace PKISharp.WACS.Plugins.Resolvers }; // Apply default sorting when no sorting has been provided yet - if (!(options is IOrderedEnumerable<T>)) - { - options = options.OrderBy(x => x.Order).ThenBy(x => x.Description); - } + var options = _plugins.GetFactories<T>(scope); + options = filter != null ? filter(options) : options.Where(x => !(x is INull)); + options = sort != null ? sort(options) : options.OrderBy(x => x.Order).ThenBy(x => x.Description); + var localOptions = options. Select(x => new { plugin = x, @@ -84,7 +85,9 @@ namespace PKISharp.WACS.Plugins.Resolvers }); // Default out when there are no reasonable options to pick - if (!localOptions.Any() || localOptions.All(x => x.disabled.Item1)) + if (!localOptions.Any() || + localOptions.All(x => x.disabled.Item1) || + localOptions.All(x => x.plugin is INull)) { return nullResult; } @@ -94,7 +97,7 @@ namespace PKISharp.WACS.Plugins.Resolvers var showMenu = _runLevel.HasFlag(RunLevel.Advanced); if (!string.IsNullOrEmpty(defaultParam1)) { - var defaultPlugin = resolver(scope, defaultParam1, defaultParam2); + var defaultPlugin = _plugins.GetFactory<T>(scope, defaultParam1, defaultParam2); if (defaultPlugin == null) { _log.Error("Unable to find {n} plugin {p}", className, defaultParam1); @@ -106,10 +109,14 @@ namespace PKISharp.WACS.Plugins.Resolvers } } - var defaultTypeDisabled = localOptions.First(x => x.type == defaultType).disabled; + var defaultOption = localOptions.First(x => x.type == defaultType); + var defaultTypeDisabled = defaultOption.disabled; if (defaultTypeDisabled.Item1) { - _log.Warning("Default {n} plugin not available: {m}", className, defaultTypeDisabled.Item2); + _log.Warning("{n} plugin {x} not available: {m}", + char.ToUpper(className[0]) + className.Substring(1), + defaultOption.plugin.Name, + defaultTypeDisabled.Item2); defaultType = defaultTypeFallback; showMenu = true; } @@ -158,10 +165,8 @@ namespace PKISharp.WACS.Plugins.Resolvers /// <returns></returns> public override async Task<ITargetPluginOptionsFactory> GetTargetPlugin(ILifetimeScope scope) { - return await GetPlugin( + return await GetPlugin<ITargetPluginOptionsFactory>( scope, - _plugins.TargetPluginFactories(scope).Where(x => !(x is INull)), - (ILifetimeScope s, string p1, string? p2) => _plugins.TargetPluginFactory(s, p1), defaultParam1: _settings.Target.DefaultTarget, defaultType: typeof(IISOptionsFactory), defaultTypeFallback: typeof(ManualOptionsFactory), @@ -179,11 +184,12 @@ namespace PKISharp.WACS.Plugins.Resolvers /// <returns></returns> public override async Task<IValidationPluginOptionsFactory> GetValidationPlugin(ILifetimeScope scope, Target target) { - return await GetPlugin( + return await GetPlugin<IValidationPluginOptionsFactory>( scope, - _plugins.ValidationPluginFactories(scope). - Where(x => !(x is INull)). - OrderBy(x => { + sort: x => + x. + OrderBy(x => + { return x.ChallengeType switch { Constants.Http01ChallengeType => 0, @@ -192,9 +198,8 @@ namespace PKISharp.WACS.Plugins.Resolvers _ => 3, }; }). - ThenBy(x => x.Order). - ThenBy(x => x.Description), - (ILifetimeScope s, string p1, string? p2) => _plugins.ValidationPluginFactory(s, p1, p2), + ThenBy(x => x.Order). + ThenBy(x => x.Description), unusable: x => (!x.CanValidate(target), "Unsuppored target. Most likely this is because you have included a wildcard identifier (*.example.com), which requires DNS validation."), description: x => $"[{x.ChallengeType}] {x.Description}", defaultParam1: _settings.Validation.DefaultValidation, @@ -212,10 +217,8 @@ namespace PKISharp.WACS.Plugins.Resolvers public override async Task<ICsrPluginOptionsFactory> GetCsrPlugin(ILifetimeScope scope) { - return await GetPlugin( + return await GetPlugin<ICsrPluginOptionsFactory>( scope, - _plugins.CsrPluginOptionsFactories(scope).Where(x => !(x is INull)), - (ILifetimeScope s, string p1, string? p2) => _plugins.CsrPluginFactory(s, p1), defaultParam1: _settings.Csr.DefaultCsr, defaultType: typeof(RsaOptionsFactory), defaultTypeFallback: typeof(EcOptionsFactory), @@ -245,11 +248,15 @@ namespace PKISharp.WACS.Plugins.Resolvers shortDescription = "Would you like to store it in another way too?"; defaultType = typeof(NullStoreOptionsFactory); } - return await GetPlugin( + var defaultParam1 = _settings.Store.DefaultStore; + var csv = defaultParam1.ParseCsv(); + defaultParam1 = csv?.Count > chosen.Count() ? + csv[chosen.Count()] : + ""; + return await GetPlugin<IStorePluginOptionsFactory>( scope, - _plugins.StorePluginFactories(scope).Except(chosen), - (ILifetimeScope s, string p1, string? p2) => _plugins.StorePluginFactory(s, p1), - defaultParam1: _settings.Csr.DefaultCsr, + filter: (x) => x.Except(chosen), + defaultParam1: defaultParam1, defaultType: defaultType, defaultTypeFallback: typeof(PemFilesOptionsFactory), nullResult: new NullStoreOptionsFactory(), @@ -283,12 +290,16 @@ namespace PKISharp.WACS.Plugins.Resolvers shortDescription = "Add another installation step?"; defaultType = typeof(NullInstallationOptionsFactory); } - return await GetPlugin( + var defaultParam1 = _settings.Installation.DefaultInstallation; + var csv = defaultParam1.ParseCsv(); + defaultParam1 = csv?.Count > chosen.Count() ? + csv[chosen.Count()] : + ""; + return await GetPlugin<IInstallationPluginOptionsFactory>( scope, - _plugins.InstallationPluginFactories(scope).Except(chosen), - (ILifetimeScope s, string p1, string? p2) => _plugins.InstallationPluginFactory(s, p1), - unusable: x => (!x.CanInstall(storeTypes), "This step be used with the specified stores"), - defaultParam1: _settings.Installation.DefaultInstallation, + filter: (x) => x.Except(chosen), + unusable: x => (!x.CanInstall(storeTypes), "This step cannot be used in combination with the specified store(s)"), + defaultParam1: defaultParam1, defaultType: defaultType, defaultTypeFallback: typeof(NullInstallationOptionsFactory), nullResult: new NullInstallationOptionsFactory(), diff --git a/src/main.lib/Plugins/Resolvers/UnattendedResolver.cs b/src/main.lib/Plugins/Resolvers/UnattendedResolver.cs index 0c7a73f..a4d0d09 100644 --- a/src/main.lib/Plugins/Resolvers/UnattendedResolver.cs +++ b/src/main.lib/Plugins/Resolvers/UnattendedResolver.cs @@ -6,6 +6,7 @@ using PKISharp.WACS.Plugins.CsrPlugins; using PKISharp.WACS.Plugins.Interfaces; using PKISharp.WACS.Plugins.OrderPlugins; using PKISharp.WACS.Plugins.StorePlugins; +using PKISharp.WACS.Plugins.TargetPlugins; using PKISharp.WACS.Plugins.ValidationPlugins.Http; using PKISharp.WACS.Services; using System; @@ -30,9 +31,84 @@ namespace PKISharp.WACS.Plugins.Resolvers _settings = settings; } + private async Task<T> GetPlugin<T>( + ILifetimeScope scope, + Type defaultType, + T nullResult, + string className, + string? defaultParam1 = null, + string? defaultParam2 = null, + Func<IEnumerable<T>, IEnumerable<T>>? filter = null, + Func<T, (bool, string?)>? unusable = null) where T: IPluginOptionsFactory + { + // Helper method to determine final usability state + // combination of plugin being enabled (e.g. due to missing + // administrator rights) and being a right fit for the current + // renewal (e.g. cannot validate wildcards using http-01) + (bool, string?) disabledOrUnusable(T plugin) + { + var disabled = (plugin as IPluginOptionsFactory)?.Disabled ?? (true, "Invalid plugin"); + if (disabled.Item1) + { + return disabled; + } + else if (unusable != null) + { + return unusable(plugin); + } + return (false, null); + }; + + // Apply default sorting when no sorting has been provided yet + var options = _plugins.GetFactories<T>(scope); + options = filter != null ? filter(options) : options.Where(x => !(x is INull)); + var localOptions = options. + Select(x => new { + plugin = x, + type = x?.GetType(), + disabled = disabledOrUnusable(x) + }); + + // Default out when there are no reasonable options to pick + if (!localOptions.Any() || + localOptions.All(x => x.disabled.Item1) || + localOptions.All(x => x.plugin is INull)) + { + return nullResult; + } + + var changeInstructions = $"Choose another plugin using the --{className} switch or change the default in settings.json"; + if (!string.IsNullOrEmpty(defaultParam1)) + { + var defaultPlugin = _plugins.GetFactory<T>(scope, defaultParam1, defaultParam2); + if (defaultPlugin == null) + { + _log.Error("Unable to find {n} plugin {p}. " + changeInstructions, className, defaultParam1); + return nullResult; + } + else + { + defaultType = defaultPlugin.GetType(); + } + } + + var defaultOption = localOptions.First(x => x.type == defaultType); + var defaultTypeDisabled = defaultOption.disabled; + if (defaultTypeDisabled.Item1) + { + _log.Error("{n} plugin {x} not available: {m}. " + changeInstructions, + char.ToUpper(className[0]) + className.Substring(1), + (defaultOption.plugin as IPluginOptionsFactory)?.Name ?? "Unknown", + defaultTypeDisabled.Item2); + return nullResult; + } + + return (T)scope.Resolve(defaultType); + } + /// <summary> /// Get the TargetPlugin which was used (or can be assumed to have been used) to create this - /// ScheduledRenewal + /// Renewal /// </summary> /// <returns></returns> public virtual async Task<ITargetPluginOptionsFactory> GetTargetPlugin(ILifetimeScope scope) @@ -41,58 +117,32 @@ namespace PKISharp.WACS.Plugins.Resolvers // sense because MainArguments.Target is what triggers // unattended mode in the first place. We woudn't even // get into this code unless it was specified. - - // Get plugin factory - if (string.IsNullOrEmpty(_arguments.MainArguments.Target)) - { - return new NullTargetFactory(); - } - var targetPluginFactory = _plugins.TargetPluginFactory(scope, _arguments.MainArguments.Target); - if (targetPluginFactory == null) - { - _log.Error("Unable to find target plugin {PluginName}", _arguments.MainArguments.Target); - return new NullTargetFactory(); - } - var (disabled, disabledReason) = targetPluginFactory.Disabled; - if (disabled) - { - _log.Error($"Target plugin {{PluginName}} is not available. {disabledReason}", _arguments.MainArguments.Target); - return new NullTargetFactory(); - } - return targetPluginFactory; + return await GetPlugin<ITargetPluginOptionsFactory>( + scope, + defaultParam1: _arguments.MainArguments.Target, + defaultType: typeof(ManualOptionsFactory), + nullResult: new NullTargetFactory(), + className: "target"); } /// <summary> /// Get the ValidationPlugin which was used (or can be assumed to have been used) - /// to validate this ScheduledRenewal + /// to validate this Renewal /// </summary> /// <returns></returns> public virtual async Task<IValidationPluginOptionsFactory> GetValidationPlugin(ILifetimeScope scope, Target target) { - // Get plugin factory - var validationPluginFactory = string.IsNullOrEmpty(_arguments.MainArguments.Validation) - ? scope.Resolve<SelfHostingOptionsFactory>() - : _plugins.ValidationPluginFactory(scope, - _arguments.MainArguments.ValidationMode ?? Constants.Http01ChallengeType, - _arguments.MainArguments.Validation); - - if (validationPluginFactory == null) - { - _log.Error("Unable to find validation plugin {PluginName}", _arguments.MainArguments.Validation); - return new NullValidationFactory(); - } - var (disabled, disabledReason) = validationPluginFactory.Disabled; - if (disabled) - { - _log.Error($"Validation plugin {{PluginName}} is not available. {disabledReason}", validationPluginFactory.Name); - return new NullValidationFactory(); - } - if (!validationPluginFactory.CanValidate(target)) - { - _log.Error("Validation plugin {PluginName} cannot validate this target", validationPluginFactory.Name); - return new NullValidationFactory(); - } - return validationPluginFactory; + return await GetPlugin<IValidationPluginOptionsFactory>( + scope, + defaultParam1: _arguments.MainArguments.Validation ?? + _settings.Validation.DefaultValidation, + defaultParam2: _arguments.MainArguments.ValidationMode ?? + _settings.Validation.DefaultValidationMode ?? + Constants.Http01ChallengeType, + defaultType: typeof(SelfHostingOptionsFactory), + nullResult: new NullValidationFactory(), + unusable: (c) => (!c.CanValidate(target), "Unsuppored target. Most likely this is because you have included a wildcard identifier (*.example.com), which requires DNS validation."), + className: "validation"); } /// <summary> @@ -102,24 +152,12 @@ namespace PKISharp.WACS.Plugins.Resolvers /// <returns></returns> public virtual async Task<IOrderPluginOptionsFactory> GetOrderPlugin(ILifetimeScope scope, Target target) { - var pluginName = _arguments.MainArguments.Order ?? _settings.Order.DefaultPlugin; - if (string.IsNullOrEmpty(pluginName)) - { - return scope.Resolve<SingleOptionsFactory>(); - } - var factory = _plugins.OrderPluginFactory(scope, pluginName); - var changeInstructions = "choose another plugin using the --order switch or change the default in settings.json"; - if (factory == null) - { - _log.Error("Unable to find order plugin {PluginName}, " + changeInstructions, pluginName); - return new NullOrderOptionsFactory(); - } - if (!factory.CanProcess(target)) - { - _log.Error("Order plugin {PluginName} cannot process this target, " + changeInstructions, factory.Name); - return new NullOrderOptionsFactory(); - } - return factory; + return await GetPlugin<IOrderPluginOptionsFactory>( + scope, + defaultParam1: _arguments.MainArguments.Order, + defaultType: typeof(SingleOptionsFactory), + nullResult: new NullOrderOptionsFactory(), + className: "order"); } /// <summary> @@ -129,24 +167,12 @@ namespace PKISharp.WACS.Plugins.Resolvers /// <returns></returns> public virtual async Task<ICsrPluginOptionsFactory> GetCsrPlugin(ILifetimeScope scope) { - var pluginName = _arguments.MainArguments.Csr; - if (string.IsNullOrEmpty(pluginName)) - { - return scope.Resolve<RsaOptionsFactory>(); - } - var factory = _plugins.CsrPluginFactory(scope, pluginName); - if (factory == null) - { - _log.Error("Unable to find csr plugin {PluginName}", pluginName); - return new NullCsrFactory(); - } - var (disabled, disabledReason) = factory.Disabled; - if (disabled) - { - _log.Error($"CSR plugin {{PluginName}} is not available. {disabledReason}", pluginName); - return new NullCsrFactory(); - } - return factory; + return await GetPlugin<ICsrPluginOptionsFactory>( + scope, + defaultParam1: _arguments.MainArguments.Csr, + defaultType: typeof(RsaOptionsFactory), + nullResult: new NullCsrFactory(), + className: "csr"); } /// <summary> @@ -155,45 +181,30 @@ namespace PKISharp.WACS.Plugins.Resolvers /// <returns></returns> public virtual async Task<IStorePluginOptionsFactory?> GetStorePlugin(ILifetimeScope scope, IEnumerable<IStorePluginOptionsFactory> chosen) { - var args = _arguments.MainArguments.Store; - if (string.IsNullOrEmpty(args)) + var cmd = _arguments.MainArguments.Store ?? _settings.Store.DefaultStore; + if (string.IsNullOrEmpty(cmd)) { - if (chosen.Count() == 0) - { - args = CertificateStoreOptions.PluginName; - } - else - { - return new NullStoreOptionsFactory(); - } + cmd = CertificateStoreOptions.PluginName; } - - var parts = args.ParseCsv(); + var parts = cmd.ParseCsv(); if (parts == null) { return null; } - var index = chosen.Count(); if (index == parts.Count) { return new NullStoreOptionsFactory(); } - - var name = parts[index]; - var factory = _plugins.StorePluginFactory(scope, name); - if (factory == null) - { - _log.Error("Unable to find store plugin {PluginName}", name); - return null; - } - var (disabled, disabledReason) = factory.Disabled; - if (disabled) - { - _log.Error($"Store plugin {{PluginName}} is not available. {disabledReason}", name); - return null; - } - return factory; + return await GetPlugin<IStorePluginOptionsFactory>( + scope, + filter: x => x, + defaultParam1: parts[index], + defaultType: typeof(NullStoreOptionsFactory), +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + nullResult: default, +#pragma warning restore CS8625 + className: "store"); } /// <summary> @@ -203,39 +214,27 @@ namespace PKISharp.WACS.Plugins.Resolvers /// <returns></returns> public virtual async Task<IInstallationPluginOptionsFactory?> GetInstallationPlugin(ILifetimeScope scope, IEnumerable<Type> storeTypes, IEnumerable<IInstallationPluginOptionsFactory> chosen) { - if (string.IsNullOrEmpty(_arguments.MainArguments.Installation)) + var cmd = _arguments.MainArguments.Installation ?? _settings.Installation.DefaultInstallation; + var parts = cmd.ParseCsv(); + if (parts == null) { return new NullInstallationOptionsFactory(); } - else + var index = chosen.Count(); + if (index == parts.Count) { - var parts = _arguments.MainArguments.Installation.ParseCsv(); - var index = chosen.Count(); - if (parts == null || index == parts.Count) - { - return new NullInstallationOptionsFactory(); - } - - var name = parts[index]; - var factory = _plugins.InstallationPluginFactory(scope, name); - if (factory == null) - { - _log.Error("Unable to find installation plugin {PluginName}", name); - return null; - } - var (disabled, disabledReason) = factory.Disabled; - if (disabled) - { - _log.Error($"Installation plugin {{PluginName}} is not available. {disabledReason}", name); - return null; - } - if (!factory.CanInstall(storeTypes)) - { - _log.Error("Installation plugin {PluginName} cannot install from selected store(s)", name); - return null; - } - return factory; + return new NullInstallationOptionsFactory(); } + return await GetPlugin<IInstallationPluginOptionsFactory>( + scope, + filter: x => x, + unusable: x => (!x.CanInstall(storeTypes), "This step cannot be used in combination with the specified store(s)"), + defaultParam1: parts[index], + defaultType: typeof(NullInstallationOptionsFactory), +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + nullResult: default, +#pragma warning restore CS8625 + className: "store"); } } } diff --git a/src/main.lib/Services/ArgumentsParser.cs b/src/main.lib/Services/ArgumentsParser.cs index 0c974f1..20529f0 100644 --- a/src/main.lib/Services/ArgumentsParser.cs +++ b/src/main.lib/Services/ArgumentsParser.cs @@ -9,7 +9,7 @@ namespace PKISharp.WACS.Configuration { private readonly ILogService _log; private readonly string[] _args; - private readonly List<IArgumentsProvider> _providers; + private readonly IEnumerable<IArgumentsProvider> _providers; public T GetArguments<T>() where T : class, new() { @@ -27,7 +27,7 @@ namespace PKISharp.WACS.Configuration { _log = log; _args = args; - _providers = plugins.OptionProviders(); + _providers = plugins.ArgumentsProviders(); } internal bool Validate() diff --git a/src/main.lib/Services/AutofacBuilder.cs b/src/main.lib/Services/AutofacBuilder.cs index 6f658a9..5a5e4f9 100644 --- a/src/main.lib/Services/AutofacBuilder.cs +++ b/src/main.lib/Services/AutofacBuilder.cs @@ -164,7 +164,7 @@ namespace PKISharp.WACS.Services builder.Register(x => { var plugin = x.Resolve<IPluginService>(); - var match = plugin.ValidationPluginFactories(target).FirstOrDefault(vp => vp.OptionsType.PluginId() == renewal.ValidationPluginOptions.Plugin); + var match = plugin.GetFactories<IValidationPluginOptionsFactory>(target).FirstOrDefault(vp => vp.OptionsType.PluginId() == renewal.ValidationPluginOptions.Plugin); return match; }).As<IValidationPluginOptionsFactory>().SingleInstance(); diff --git a/src/main.lib/Services/Interfaces/IPluginService.cs b/src/main.lib/Services/Interfaces/IPluginService.cs index 02a4793..4ef77b4 100644 --- a/src/main.lib/Services/Interfaces/IPluginService.cs +++ b/src/main.lib/Services/Interfaces/IPluginService.cs @@ -8,21 +8,9 @@ namespace PKISharp.WACS.Services { public interface IPluginService { - List<ITargetPluginOptionsFactory> TargetPluginFactories(ILifetimeScope scope); - List<IValidationPluginOptionsFactory> ValidationPluginFactories(ILifetimeScope scope); - List<IOrderPluginOptionsFactory> OrderPluginFactories(ILifetimeScope scope); - List<ICsrPluginOptionsFactory> CsrPluginOptionsFactories(ILifetimeScope scope); - List<IStorePluginOptionsFactory> StorePluginFactories(ILifetimeScope scope); - List<IInstallationPluginOptionsFactory> InstallationPluginFactories(ILifetimeScope scope); - - ITargetPluginOptionsFactory TargetPluginFactory(ILifetimeScope scope, string name); - IValidationPluginOptionsFactory ValidationPluginFactory(ILifetimeScope scope, string type, string name); - IOrderPluginOptionsFactory OrderPluginFactory(ILifetimeScope scope, string name); - ICsrPluginOptionsFactory CsrPluginFactory(ILifetimeScope scope, string name); - IStorePluginOptionsFactory StorePluginFactory(ILifetimeScope scope, string name); - IInstallationPluginOptionsFactory InstallationPluginFactory(ILifetimeScope scope, string name); - - List<IArgumentsProvider> OptionProviders(); - List<Type> PluginOptionTypes<T>() where T : PluginOptions; + IEnumerable<T> GetFactories<T>(ILifetimeScope scope) where T: IPluginOptionsFactory; + T GetFactory<T>(ILifetimeScope scope, string name, string? parameter = null) where T : IPluginOptionsFactory; + IEnumerable<IArgumentsProvider> ArgumentsProviders(); + IEnumerable<Type> PluginOptionTypes<T>() where T : PluginOptions; } } diff --git a/src/main.lib/Services/PluginService.cs b/src/main.lib/Services/PluginService.cs index b2c4a57..404e65d 100644 --- a/src/main.lib/Services/PluginService.cs +++ b/src/main.lib/Services/PluginService.cs @@ -14,114 +14,53 @@ namespace PKISharp.WACS.Services public class PluginService : IPluginService { private readonly List<Type> _allTypes; - - private readonly List<Type> _optionProviders; - - private readonly List<Type> _targetOptionFactories; - private readonly List<Type> _validationOptionFactories; - private readonly List<Type> _orderOptionFactories; - private readonly List<Type> _csrOptionFactories; - private readonly List<Type> _storeOptionFactories; - private readonly List<Type> _installationOptionFactories; - - private readonly List<Type> _target; - private readonly List<Type> _validation; - private readonly List<Type> _order; - private readonly List<Type> _csr; - private readonly List<Type> _store; - private readonly List<Type> _installation; + private readonly List<Type> _argumentProviders; + private readonly List<Type> _optionFactories; + private readonly List<Type> _plugins; internal readonly ILogService _log; - public List<IArgumentsProvider> OptionProviders() + public IEnumerable<IArgumentsProvider> ArgumentsProviders() { - return _optionProviders.Select(x => + return _argumentProviders.Select(x => { var c = x.GetConstructor(new Type[] { }); return (IArgumentsProvider)c.Invoke(new object[] { }); }).ToList(); } - public List<ITargetPluginOptionsFactory> TargetPluginFactories(ILifetimeScope scope) => GetFactories<ITargetPluginOptionsFactory>(_targetOptionFactories, scope); - - public List<IValidationPluginOptionsFactory> ValidationPluginFactories(ILifetimeScope scope) => GetFactories<IValidationPluginOptionsFactory>(_validationOptionFactories, scope); - - public List<IOrderPluginOptionsFactory> OrderPluginFactories(ILifetimeScope scope) => GetFactories<IOrderPluginOptionsFactory>(_orderOptionFactories, scope); - - public List<ICsrPluginOptionsFactory> CsrPluginOptionsFactories(ILifetimeScope scope) => GetFactories<ICsrPluginOptionsFactory>(_csrOptionFactories, scope); - - public List<IStorePluginOptionsFactory> StorePluginFactories(ILifetimeScope scope) => GetFactories<IStorePluginOptionsFactory>(_storeOptionFactories, scope); - - public List<IInstallationPluginOptionsFactory> InstallationPluginFactories(ILifetimeScope scope) => GetFactories<IInstallationPluginOptionsFactory>(_installationOptionFactories, scope); - - public ITargetPluginOptionsFactory TargetPluginFactory(ILifetimeScope scope, string name) => GetByName<ITargetPluginOptionsFactory>(_targetOptionFactories, name, scope); - - public IValidationPluginOptionsFactory ValidationPluginFactory(ILifetimeScope scope, string type, string name) - { - return _validationOptionFactories. - Select(scope.Resolve). - OfType<IValidationPluginOptionsFactory>(). - FirstOrDefault(x => x.Match(name) && string.Equals(type, x.ChallengeType, StringComparison.InvariantCultureIgnoreCase)); - } - - public IOrderPluginOptionsFactory OrderPluginFactory(ILifetimeScope scope, string name) => GetByName<IOrderPluginOptionsFactory>(_orderOptionFactories, name, scope); - - public ICsrPluginOptionsFactory CsrPluginFactory(ILifetimeScope scope, string name) => GetByName<ICsrPluginOptionsFactory>(_csrOptionFactories, name, scope); - - public IStorePluginOptionsFactory StorePluginFactory(ILifetimeScope scope, string name) => GetByName<IStorePluginOptionsFactory>(_storeOptionFactories, name, scope); - - public IInstallationPluginOptionsFactory InstallationPluginFactory(ILifetimeScope scope, string name) => GetByName<IInstallationPluginOptionsFactory>(_installationOptionFactories, name, scope); - - public List<Type> PluginOptionTypes<T>() where T : PluginOptions => GetResolvable<T>(); + public IEnumerable<Type> PluginOptionTypes<T>() where T : PluginOptions => GetResolvable<T>(); internal void Configure(ContainerBuilder builder) { - _targetOptionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); - _validationOptionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); - _orderOptionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); - _csrOptionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); - _storeOptionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); - _installationOptionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); - - _target.ForEach(ip => builder.RegisterType(ip)); - _validation.ForEach(ip => builder.RegisterType(ip)); - _order.ForEach(ip => builder.RegisterType(ip)); - _csr.ForEach(ip => builder.RegisterType(ip)); - _store.ForEach(ip => builder.RegisterType(ip)); - _installation.ForEach(ip => builder.RegisterType(ip)); + _optionFactories.ForEach(t => builder.RegisterType(t).SingleInstance()); + _plugins.ForEach(ip => builder.RegisterType(ip)); } - private List<T> GetFactories<T>(List<Type> source, ILifetimeScope scope) where T : IPluginOptionsFactory => source.Select(scope.Resolve).OfType<T>().OrderBy(x => x.Order).ToList(); + public IEnumerable<T> GetFactories<T>(ILifetimeScope scope) where T : IPluginOptionsFactory => _optionFactories.Select(scope.Resolve).OfType<T>().OrderBy(x => x.Order).ToList(); - private T GetByName<T>(IEnumerable<Type> list, string name, ILifetimeScope scope) where T : IPluginOptionsFactory => list.Select(scope.Resolve).OfType<T>().FirstOrDefault(x => x.Match(name)); + private IEnumerable<T> GetByName<T>(string name, ILifetimeScope scope) where T : IPluginOptionsFactory => GetFactories<T>(scope).Where(x => x.Match(name)); public PluginService(ILogService logger) { _log = logger; _allTypes = GetTypes(); - _optionProviders = GetResolvable<IArgumentsProvider>(); - - _targetOptionFactories = GetResolvable<ITargetPluginOptionsFactory>(); - _validationOptionFactories = GetResolvable<IValidationPluginOptionsFactory>(); - _orderOptionFactories = GetResolvable<IOrderPluginOptionsFactory>(); - _csrOptionFactories = GetResolvable<ICsrPluginOptionsFactory>(); - _storeOptionFactories = GetResolvable<IStorePluginOptionsFactory>(true); - _installationOptionFactories = GetResolvable<IInstallationPluginOptionsFactory>(true); - - _target = GetResolvable<ITargetPlugin>(); - _validation = GetResolvable<IValidationPlugin>(); - _order = GetResolvable<IOrderPlugin>(); - _csr = GetResolvable<ICsrPlugin>(); - _store = GetResolvable<IStorePlugin>(); - _installation = GetResolvable<IInstallationPlugin>(); - - ListPlugins(_target, "target"); - ListPlugins(_validation, "validation"); - ListPlugins(_order, "order"); - ListPlugins(_csr, "csr"); - ListPlugins(_store, "store"); - ListPlugins(_installation, "installation"); + _argumentProviders = GetResolvable<IArgumentsProvider>(); + _optionFactories = GetResolvable<IPluginOptionsFactory>(true); + _plugins = new List<Type>(); + void AddPluginType<T>(string name) + { + var temp = GetResolvable<T>(); + ListPlugins(temp, name); + _plugins.AddRange(temp); + } + AddPluginType<ITargetPlugin>("target"); + AddPluginType<IValidationPlugin>("validation"); + AddPluginType<IOrderPlugin>("order"); + AddPluginType<ICsrPlugin>("csr"); + AddPluginType<IStorePlugin>("store"); + AddPluginType<IInstallationPlugin>("installation"); } private void ListPlugins(IEnumerable<Type> list, string type) @@ -268,5 +207,15 @@ namespace PKISharp.WACS.Services } return ret.ToList(); } + + public T GetFactory<T>(ILifetimeScope scope, string name, string? parameter = null) where T : IPluginOptionsFactory + { + var plugins = GetByName<T>(name, scope); + if (typeof(T) is IValidationPluginOptionsFactory) + { + plugins = plugins.Where(x => string.Equals(parameter, (x as IValidationPluginOptionsFactory)?.ChallengeType, StringComparison.InvariantCultureIgnoreCase)); + } + return plugins.FirstOrDefault(); + } } } diff --git a/src/main.lib/Services/SettingsService.cs b/src/main.lib/Services/SettingsService.cs index e89b111..0fb54c5 100644 --- a/src/main.lib/Services/SettingsService.cs +++ b/src/main.lib/Services/SettingsService.cs @@ -549,7 +549,7 @@ namespace PKISharp.WACS.Services /// <summary> /// Default plugin(s) to select /// </summary> - public string? DefaultPlugin { get; set; } + public string? DefaultStore { get; set; } /// <summary> /// The certificate store to save the certificates in. If left empty, diff --git a/src/main.test/Mock/Clients/IISClient.cs b/src/main.test/Mock/Clients/IISClient.cs index 6b8cc38..df0c1a8 100644 --- a/src/main.test/Mock/Clients/IISClient.cs +++ b/src/main.test/Mock/Clients/IISClient.cs @@ -121,7 +121,7 @@ namespace PKISharp.WACS.UnitTests.Mock.Clients public void UpdateBinding(MockSite site, MockBinding binding, BindingOptions bindingOptions) { - site.Bindings.Remove(binding); + _ = site.Bindings.Remove(binding); var updateOptions = bindingOptions .WithHost(binding.Host) .WithIP(binding.IP) diff --git a/src/main.test/Tests/BindingTests/Bindings.cs b/src/main.test/Tests/BindingTests/Bindings.cs index 138c481..8b4a57a 100644 --- a/src/main.test/Tests/BindingTests/Bindings.cs +++ b/src/main.test/Tests/BindingTests/Bindings.cs @@ -171,16 +171,16 @@ namespace PKISharp.WACS.UnitTests.Tests.BindingTests Assert.AreEqual(existingBindings.Count() + expectedNew, httpOnlySite.Bindings.Count); var newBindings = httpOnlySite.Bindings.Except(existingBindings); - newBindings.All(newBinding => - { - Assert.AreEqual(existingHost, newBinding.Host); - Assert.AreEqual("https", newBinding.Protocol); - Assert.AreEqual(storeName, newBinding.CertificateStoreName); - Assert.AreEqual(newCert, newBinding.CertificateHash); - Assert.AreEqual(bindingPort, newBinding.Port); - Assert.AreEqual(expectedFlags, newBinding.SSLFlags); - return true; - }); + _ = newBindings.All(newBinding => + { + Assert.AreEqual(existingHost, newBinding.Host); + Assert.AreEqual("https", newBinding.Protocol); + Assert.AreEqual(storeName, newBinding.CertificateStoreName); + Assert.AreEqual(newCert, newBinding.CertificateHash); + Assert.AreEqual(bindingPort, newBinding.Port); + Assert.AreEqual(expectedFlags, newBinding.SSLFlags); + return true; + }); var oldips = existingBindings.Select(x => x.IP).Distinct(); var newips = newBindings.Select(x => x.IP).Distinct(); diff --git a/src/main.test/Tests/InstallationPluginTests/MultipleInstallerTests.cs b/src/main.test/Tests/InstallationPluginTests/MultipleInstallerTests.cs index 2e22240..8c8aa79 100644 --- a/src/main.test/Tests/InstallationPluginTests/MultipleInstallerTests.cs +++ b/src/main.test/Tests/InstallationPluginTests/MultipleInstallerTests.cs @@ -12,6 +12,7 @@ using mock = PKISharp.WACS.UnitTests.Mock.Services; using System; using System.Collections.Generic; using System.Threading.Tasks; +using PKISharp.WACS.Clients.DNS; namespace PKISharp.WACS.UnitTests.Tests.InstallationPluginTests { @@ -42,26 +43,30 @@ namespace PKISharp.WACS.UnitTests.Tests.InstallationPluginTests var builder = new ContainerBuilder(); - builder.RegisterInstance(plugins). + _ = builder.RegisterType<LookupClientProvider>(); + _ = builder.RegisterType<ProxyService>(); + _ = builder.RegisterType<DomainParseService>(); + _ = builder.RegisterType<IISHelper>(); + _ = builder.RegisterInstance(plugins). As<IPluginService>(). - SingleInstance(); - builder.RegisterInstance(settings). + SingleInstance(); + _ = builder.RegisterInstance(settings). As<ISettingsService>(). SingleInstance(); - builder.RegisterInstance(log). + _ = builder.RegisterInstance(log). As<ILogService>(). SingleInstance(); - builder.RegisterType<Mock.Clients.MockIISClient>(). + _ = builder.RegisterType<Mock.Clients.MockIISClient>(). As<IIISClient>(). SingleInstance(); - builder.RegisterType<ArgumentsParser>(). + _ = builder.RegisterType<ArgumentsParser>(). SingleInstance(). WithParameter(new TypedParameter(typeof(string[]), commandLine.Split(' '))); - builder.RegisterType<ArgumentsService>(). + _ = builder.RegisterType<ArgumentsService>(). As<IArgumentsService>(). SingleInstance(); - builder.RegisterType<mock.UserRoleService>().As<IUserRoleService>().SingleInstance(); - builder.RegisterType<UnattendedResolver>().As<IResolver>(); + _ = builder.RegisterType<mock.UserRoleService>().As<IUserRoleService>().SingleInstance(); + _ = builder.RegisterType<UnattendedResolver>().As<IResolver>(); plugins.Configure(builder); var scope = builder.Build(); diff --git a/src/main/settings.json b/src/main/settings.json index 332569b..7657b14 100644 --- a/src/main/settings.json +++ b/src/main/settings.json @@ -59,7 +59,7 @@ "DefaultTarget": null }, "Validation": { - "DefaultValidation": "azure", + "DefaultValidation": "script", "DefaultValidationMode": "dns-01", "CleanupFolders": true, "PreValidateDns": true, |