summaryrefslogtreecommitdiffstats
path: root/src/main.lib/Plugins/TargetPlugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.lib/Plugins/TargetPlugins')
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISBinding/IISBinding.cs54
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArguments.cs8
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArgumentsProvider.cs28
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptions.cs30
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptionsFactory.cs84
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSite/IISSite.cs57
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArguments.cs9
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArgumentsProvider.cs31
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptions.cs32
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsFactory.cs85
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsHelper.cs77
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSites/IISSites.cs69
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptions.cs40
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptionsFactory.cs133
-rw-r--r--src/main.lib/Plugins/TargetPlugins/Manual/Manual.cs33
-rw-r--r--src/main.lib/Plugins/TargetPlugins/Manual/ManualArguments.cs8
-rw-r--r--src/main.lib/Plugins/TargetPlugins/Manual/ManualArgumentsProvider.cs27
-rw-r--r--src/main.lib/Plugins/TargetPlugins/Manual/ManualOptions.cs18
-rw-r--r--src/main.lib/Plugins/TargetPlugins/Manual/ManualOptionsFactory.cs64
19 files changed, 887 insertions, 0 deletions
diff --git a/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBinding.cs b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBinding.cs
new file mode 100644
index 0000000..b6125cc
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBinding.cs
@@ -0,0 +1,54 @@
+using PKISharp.WACS.Clients;
+using PKISharp.WACS.Clients.IIS;
+using PKISharp.WACS.DomainObjects;
+using PKISharp.WACS.Plugins.Interfaces;
+using PKISharp.WACS.Services;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class IISBinding : ITargetPlugin
+ {
+ private readonly ILogService _log;
+ private readonly IIISClient _iisClient;
+ private IISBindingOptions _options;
+ private readonly IISBindingHelper _helper;
+
+ public IISBinding(ILogService logService, IIISClient iisClient, IISBindingHelper helper, IISBindingOptions options)
+ {
+ _iisClient = iisClient;
+ _log = logService;
+ _options = options;
+ _helper = helper;
+ }
+
+ public Target Generate()
+ {
+ var allBindings = _helper.GetBindings(false);
+ var matchingBindings = allBindings.Where(x => x.HostUnicode == _options.Host);
+ if (matchingBindings.Count() == 0)
+ {
+ _log.Error("Binding {binding} not yet found in IIS, create it or use the Manual target plugin instead", _options.Host);
+ return null;
+ }
+ else if (!matchingBindings.Any(b => b.SiteId == _options.SiteId))
+ {
+ var newMatch = matchingBindings.First();
+ _log.Warning("Binding {binding} moved from site {a} to site {b}", _options.Host, _options.SiteId, newMatch.SiteId);
+ _options.SiteId = newMatch.SiteId;
+ }
+ return new Target()
+ {
+ FriendlyName = $"[{nameof(IISBinding)}] {_options.Host}",
+ CommonName = _options.Host,
+ Parts = new[] {
+ new TargetPart {
+ Identifiers = new List<string> { _options.Host },
+ SiteId = _options.SiteId
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArguments.cs b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArguments.cs
new file mode 100644
index 0000000..7340fb2
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArguments.cs
@@ -0,0 +1,8 @@
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class IISBindingArguments
+ {
+ public string SiteId { get; set; }
+ public string Host { get; set; }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArgumentsProvider.cs b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArgumentsProvider.cs
new file mode 100644
index 0000000..c93354e
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingArgumentsProvider.cs
@@ -0,0 +1,28 @@
+using Fclp;
+using PKISharp.WACS.Configuration;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class IISBindingArgumentsProvider : BaseArgumentsProvider<IISBindingArguments>
+ {
+ public override string Name => "IIS Binding plugin";
+ public override string Group => "Target";
+ public override string Condition => "--target iisbinding";
+
+ public override bool Active(IISBindingArguments current)
+ {
+ return !string.IsNullOrEmpty(current.SiteId) ||
+ !string.IsNullOrEmpty(current.Host);
+ }
+
+ public override void Configure(FluentCommandLineParser<IISBindingArguments> parser)
+ {
+ parser.Setup(o => o.SiteId)
+ .As("siteid")
+ .WithDescription("Id of the site where the binding should be found (optional).");
+ parser.Setup(o => o.Host)
+ .As("host")
+ .WithDescription("Host of the binding to get a certificate for.");
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptions.cs b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptions.cs
new file mode 100644
index 0000000..81b254e
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptions.cs
@@ -0,0 +1,30 @@
+using PKISharp.WACS.Plugins.Base;
+using PKISharp.WACS.Plugins.Base.Options;
+using PKISharp.WACS.Services;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ [Plugin("2f5dd428-0f5d-4c8a-8fd0-56fc1b5985ce")]
+ class IISBindingOptions : TargetPluginOptions<IISBinding>
+ {
+ public override string Name => "IISBinding";
+ public override string Description => "Single binding of an IIS website";
+
+ /// <summary>
+ /// Restrict search to a specific site
+ /// </summary>
+ public long SiteId { get; set; }
+
+ /// <summary>
+ /// Host name of the binding to look for
+ /// </summary>
+ public string Host { get; set; }
+
+ public override void Show(IInputService input)
+ {
+ base.Show(input);
+ input.Show("Host", Host, level: 1);
+ input.Show("SiteId", SiteId.ToString(), level: 1);
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptionsFactory.cs b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptionsFactory.cs
new file mode 100644
index 0000000..f5f359b
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISBinding/IISBindingOptionsFactory.cs
@@ -0,0 +1,84 @@
+using PKISharp.WACS.Clients.IIS;
+using PKISharp.WACS.Plugins.Base.Factories;
+using PKISharp.WACS.Services;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class IISBindingOptionsFactory : TargetPluginOptionsFactory<IISBinding, IISBindingOptions>
+ {
+ public override bool Hidden => !_iisClient.HasWebSites;
+ protected IIISClient _iisClient;
+ protected IISBindingHelper _helper;
+ private ILogService _log;
+ private IArgumentsService _arguments;
+
+ public IISBindingOptionsFactory(
+ ILogService log, IIISClient iisClient,
+ IISBindingHelper helper, IArgumentsService arguments)
+ {
+ _iisClient = iisClient;
+ _helper = helper;
+ _log = log;
+ _arguments = arguments;
+ }
+
+ public override IISBindingOptions Aquire(IInputService inputService, RunLevel runLevel)
+ {
+ var ret = new IISBindingOptions();
+ var bindings = _helper.GetBindings(_arguments.MainArguments.HideHttps).Where(x => !x.Hidden);
+ if (!bindings.Any())
+ {
+ _log.Error($"No sites with named bindings have been configured in IIS. Add one or choose '{ManualOptions.DescriptionText}'.");
+ return null;
+ }
+ var chosenTarget = inputService.ChooseFromList(
+ "Choose binding",
+ bindings,
+ x => Choice.Create(x),
+ "Abort");
+ if (chosenTarget != null)
+ {
+ ret.SiteId = chosenTarget.SiteId;
+ ret.Host = chosenTarget.HostUnicode;
+ return ret;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public override IISBindingOptions Default()
+ {
+ var ret = new IISBindingOptions();
+ var args = _arguments.GetArguments<IISBindingArguments>();
+ var hostName = _arguments.TryGetRequiredArgument(nameof(args.Host), args.Host).ToLower();
+ var rawSiteId = args.SiteId;
+ var filterSet = _helper.GetBindings(false);
+ if (!string.IsNullOrEmpty(rawSiteId))
+ {
+ if (long.TryParse(rawSiteId, out long siteId))
+ {
+ filterSet = filterSet.Where(x => x.SiteId == siteId).ToList();
+ }
+ else
+ {
+ _log.Error("Invalid SiteId {siteId}", rawSiteId);
+ return null;
+ }
+ }
+ var chosenTarget = filterSet.Where(x => x.HostUnicode == hostName || x.HostPunycode == hostName).FirstOrDefault();
+ if (chosenTarget != null)
+ {
+ ret.SiteId = chosenTarget.SiteId;
+ ret.Host = chosenTarget.HostUnicode;
+ return ret;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSite/IISSite.cs b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSite.cs
new file mode 100644
index 0000000..26cb8af
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSite.cs
@@ -0,0 +1,57 @@
+using PKISharp.WACS.Clients.IIS;
+using PKISharp.WACS.DomainObjects;
+using PKISharp.WACS.Plugins.Interfaces;
+using PKISharp.WACS.Services;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class IISSite : ITargetPlugin
+ {
+ protected ILogService _log;
+ protected IIISClient _iisClient;
+ protected IISSiteHelper _helper;
+ protected IISSiteOptions _options;
+
+ public IISSite(ILogService logService, IIISClient iisClient, IISSiteHelper helper, IISSiteOptions options)
+ {
+ _log = logService;
+ _iisClient = iisClient;
+ _helper = helper;
+ _options = options;
+ }
+
+ public Target Generate()
+ {
+ var site = _helper.GetSites(false, false).FirstOrDefault(s => s.Id == _options.SiteId);
+ if (site == null)
+ {
+ _log.Error($"SiteId {_options.SiteId} not found");
+ return null;
+ }
+ var hosts = site.Hosts;
+ if (_options.ExcludeBindings != null)
+ {
+ hosts = hosts.Except(_options.ExcludeBindings).ToList();
+ }
+ var cn = _options.CommonName;
+ var cnDefined = !string.IsNullOrWhiteSpace(cn);
+ var cnValid = cnDefined && hosts.Contains(cn);
+ if (cnDefined && !cnValid)
+ {
+ _log.Warning("Specified common name {cn} not valid", cn);
+ }
+ return new Target()
+ {
+ FriendlyName = $"[{nameof(IISSite)}] {site.Name}",
+ CommonName = cnValid ? cn : hosts.FirstOrDefault(),
+ Parts = new[] {
+ new TargetPart() {
+ Identifiers = hosts,
+ SiteId = site.Id
+ }
+ }
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArguments.cs b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArguments.cs
new file mode 100644
index 0000000..e03ae08
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArguments.cs
@@ -0,0 +1,9 @@
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class IISSiteArguments
+ {
+ public string SiteId { get; set; }
+ public string CommonName { get; set; }
+ public string ExcludeBindings { get; set; }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArgumentsProvider.cs b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArgumentsProvider.cs
new file mode 100644
index 0000000..0f186c0
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteArgumentsProvider.cs
@@ -0,0 +1,31 @@
+using Fclp;
+using PKISharp.WACS.Configuration;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class IISSiteArgumentsProvider : BaseArgumentsProvider<IISSiteArguments>
+ {
+ public override string Name => "IIS Site(s) plugin";
+ public override string Group => "Target";
+ public override string Condition => "--target iissite|iissites";
+ public override bool Active(IISSiteArguments current)
+ {
+ return !string.IsNullOrEmpty(current.SiteId) ||
+ !string.IsNullOrEmpty(current.CommonName) ||
+ !string.IsNullOrEmpty(current.ExcludeBindings);
+ }
+
+ public override void Configure(FluentCommandLineParser<IISSiteArguments> parser)
+ {
+ parser.Setup(o => o.SiteId)
+ .As("siteid")
+ .WithDescription("Identifier of the site that the plugin should create the target from. For iissites this may be a comma separated list.");
+ parser.Setup(o => o.CommonName)
+ .As("commonname")
+ .WithDescription("Specify the common name of the certificate that should be requested for the target. By default this will be the first binding that is enumerated.");
+ parser.Setup(o => o.ExcludeBindings)
+ .As("excludebindings")
+ .WithDescription("Exclude host names from the certificate. This may be a comma separated list.");
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptions.cs b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptions.cs
new file mode 100644
index 0000000..1051d08
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptions.cs
@@ -0,0 +1,32 @@
+using PKISharp.WACS.Plugins.Base;
+using PKISharp.WACS.Plugins.Base.Options;
+using PKISharp.WACS.Services;
+using System.Collections.Generic;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ [Plugin("d7940b23-f570-460e-ab15-2c822a79009b")]
+ class IISSiteOptions : TargetPluginOptions<IISSite>, IIISSiteOptions
+ {
+ public override string Name => "IISSite";
+ public override string Description => "All bindings of an IIS website";
+
+ public long SiteId { get; set; }
+ public string CommonName { get; set; }
+ public List<string> ExcludeBindings { get; set; }
+
+ public override void Show(IInputService input)
+ {
+ base.Show(input);
+ input.Show("SiteId", SiteId.ToString(), level: 1);
+ if (!string.IsNullOrEmpty(CommonName))
+ {
+ input.Show("CommonName", CommonName, level: 1);
+ }
+ if (ExcludeBindings != null && ExcludeBindings.Count > 0)
+ {
+ input.Show("ExcludeBindings", string.Join(",", ExcludeBindings), level: 1);
+ }
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsFactory.cs b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsFactory.cs
new file mode 100644
index 0000000..289b59c
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsFactory.cs
@@ -0,0 +1,85 @@
+using PKISharp.WACS.Clients.IIS;
+using PKISharp.WACS.Extensions;
+using PKISharp.WACS.Plugins.Base.Factories;
+using PKISharp.WACS.Services;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class IISSiteOptionsFactory : TargetPluginOptionsFactory<IISSite, IISSiteOptions>
+ {
+ public override bool Hidden => !_iisClient.HasWebSites;
+ protected IIISClient _iisClient;
+ protected IISSiteHelper _siteHelper;
+ protected IISSiteOptionsHelper _optionsHelper;
+ private readonly ILogService _log;
+ private readonly IArgumentsService _arguments;
+
+ public IISSiteOptionsFactory(
+ ILogService log, IIISClient iisClient,
+ IISSiteHelper helper, IArgumentsService arguments)
+ {
+ _iisClient = iisClient;
+ _siteHelper = helper;
+ _optionsHelper = new IISSiteOptionsHelper(log);
+ _log = log;
+ _arguments = arguments;
+ }
+
+ public override IISSiteOptions Aquire(IInputService input, RunLevel runLevel)
+ {
+ var ret = new IISSiteOptions();
+ var sites = _siteHelper.
+ GetSites(_arguments.MainArguments.HideHttps, true).
+ Where(x => x.Hidden == false).
+ Where(x => x.Hosts.Any());
+ if (!sites.Any())
+ {
+ _log.Error($"No sites with named bindings have been configured in IIS. Add one or choose '{ManualOptions.DescriptionText}'.");
+ return null;
+ }
+ var chosen = input.ChooseFromList("Choose site",
+ sites,
+ x => Choice.Create(x, x.Name),
+ "Abort");
+ if (chosen != null)
+ {
+ ret.SiteId = chosen.Id;
+ if (_optionsHelper.AquireAdvancedOptions(input, chosen.Hosts, runLevel, ret))
+ {
+ return ret;
+ }
+
+ }
+ return null;
+ }
+
+ public override IISSiteOptions Default()
+ {
+ var ret = new IISSiteOptions();
+ var args = _arguments.GetArguments<IISSiteArguments>();
+ var rawSiteId = _arguments.TryGetRequiredArgument(nameof(args.SiteId), args.SiteId);
+ if (long.TryParse(rawSiteId, out long siteId))
+ {
+ var site = _siteHelper.GetSites(false, false).FirstOrDefault(binding => binding.Id == siteId);
+ if (site != null)
+ {
+ ret.SiteId = site.Id;
+ if (_optionsHelper.DefaultAdvancedOptions(args, site.Hosts, RunLevel.Unattended, ret))
+ {
+ return ret;
+ }
+ }
+ else
+ {
+ _log.Error("Unable to find SiteId {siteId}", siteId);
+ }
+ }
+ else
+ {
+ _log.Error("Invalid SiteId {siteId}", args.SiteId);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsHelper.cs b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsHelper.cs
new file mode 100644
index 0000000..0483514
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSite/IISSiteOptionsHelper.cs
@@ -0,0 +1,77 @@
+using PKISharp.WACS.Extensions;
+using PKISharp.WACS.Services;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class IISSiteOptionsHelper
+ {
+ private ILogService _log;
+
+ public IISSiteOptionsHelper(ILogService log)
+ {
+ _log = log;
+ }
+
+ public bool AquireAdvancedOptions(IInputService input, IEnumerable<string> chosen, RunLevel runLevel, IIISSiteOptions ret)
+ {
+ if (runLevel.HasFlag(RunLevel.Advanced))
+ {
+ // Exclude bindings
+ input.WritePagedList(chosen.Select(x => Choice.Create(x, "")));
+ ret.ExcludeBindings = input.RequestString("Press enter to include all listed hosts, or type a comma-separated lists of exclusions").ParseCsv();
+ }
+
+ var remaining = chosen.Except(ret.ExcludeBindings ?? new List<string>());
+ if (remaining.Count() == 0)
+ {
+ _log.Error("No bindings remain");
+ return false;
+ }
+
+ // Set common name
+ if (remaining.Count() > 1)
+ {
+ ret.CommonName = input.ChooseFromList(
+ "Select primary domain (common name)",
+ remaining,
+ x => Choice.Create(x),
+ "Default");
+ }
+ return true;
+ }
+
+ public bool DefaultAdvancedOptions(IISSiteArguments args, IEnumerable<string> chosen, RunLevel runLevel, IIISSiteOptions ret)
+ {
+ ret.ExcludeBindings = args.ExcludeBindings.ParseCsv();
+ if (ret.ExcludeBindings != null)
+ {
+ ret.ExcludeBindings = ret.ExcludeBindings.Select(x => x.ConvertPunycode()).ToList();
+ }
+ var remaining = chosen.Except(ret.ExcludeBindings ?? new List<string>());
+ var commonName = args.CommonName;
+ if (!string.IsNullOrWhiteSpace(commonName))
+ {
+ commonName = commonName.ToLower().Trim().ConvertPunycode();
+ if (remaining.Contains(commonName))
+ {
+ ret.CommonName = commonName;
+ }
+ else
+ {
+ _log.Error("Common name {commonName} not found or excluded", commonName);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ }
+
+ public interface IIISSiteOptions
+ {
+ List<string> ExcludeBindings { get; set; }
+ string CommonName { get; set; }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSites/IISSites.cs b/src/main.lib/Plugins/TargetPlugins/IISSites/IISSites.cs
new file mode 100644
index 0000000..32b46d6
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSites/IISSites.cs
@@ -0,0 +1,69 @@
+using PKISharp.WACS.Clients.IIS;
+using PKISharp.WACS.DomainObjects;
+using PKISharp.WACS.Plugins.Interfaces;
+using PKISharp.WACS.Services;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class IISSites : ITargetPlugin
+ {
+ protected ILogService _log;
+ protected IIISClient _iisClient;
+ protected IISSiteHelper _helper;
+ protected IISSitesOptions _options;
+
+ public IISSites(ILogService log, IIISClient iisClient, IISSiteHelper helper, IISSitesOptions options)
+ {
+ _log = log;
+ _iisClient = iisClient;
+ _helper = helper;
+ _options = options;
+ }
+
+ public Target Generate()
+ {
+ var sites = _helper.GetSites(false, false);
+ var filtered = new List<IISSiteHelper.IISSiteOption>();
+ if (_options.All == true)
+ {
+ filtered = sites;
+ }
+ else
+ {
+ foreach (var id in _options.SiteIds)
+ {
+ var site = sites.FirstOrDefault(s => s.Id == id);
+ if (site != null)
+ {
+ filtered.Add(site);
+ }
+ else
+ {
+ _log.Warning("SiteId {Id} not found", id);
+ }
+ }
+ }
+ var allHosts = filtered.SelectMany(x => x.Hosts);
+ var exclude = _options.ExcludeBindings ?? new List<string>();
+ allHosts = allHosts.Except(exclude).ToList();
+ var cn = _options.CommonName;
+ var cnDefined = !string.IsNullOrWhiteSpace(cn);
+ var cnValid = cnDefined && allHosts.Contains(cn);
+ if (cnDefined && !cnValid)
+ {
+ _log.Warning("Specified common name {cn} not valid", cn);
+ }
+ return new Target()
+ {
+ FriendlyName = $"[{nameof(IISSites)}] {(_options.All == true ? "All" : string.Join(",", _options.SiteIds))}",
+ CommonName = cnValid ? cn : allHosts.FirstOrDefault(),
+ Parts = filtered.Select(site => new TargetPart {
+ Identifiers = site.Hosts.Except(exclude).ToList(),
+ SiteId = site.Id
+ })
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptions.cs b/src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptions.cs
new file mode 100644
index 0000000..61abc53
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptions.cs
@@ -0,0 +1,40 @@
+using PKISharp.WACS.Plugins.Base;
+using PKISharp.WACS.Plugins.Base.Options;
+using PKISharp.WACS.Services;
+using System.Collections.Generic;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ [Plugin("cdd79a68-4a87-4039-bee8-5a0ebdca41cb")]
+ class IISSitesOptions : TargetPluginOptions<IISSites>, IIISSiteOptions
+ {
+ public override string Name => "IISSites";
+ public override string Description => "All bindings of multiple IIS websites";
+
+ public bool? All { get; set; }
+ public List<long> SiteIds { get; set; }
+ public string CommonName { get; set; }
+ public List<string> ExcludeBindings { get; set; }
+
+ public override void Show(IInputService input)
+ {
+ base.Show(input);
+ if (All != null)
+ {
+ input.Show("SiteIds", All == true ? "All" : "None", level: 1);
+ }
+ if (SiteIds != null)
+ {
+ input.Show("SiteIds", string.Join(",", SiteIds), level: 1);
+ }
+ if (!string.IsNullOrEmpty(CommonName))
+ {
+ input.Show("CommonName", CommonName, level: 1);
+ }
+ if (ExcludeBindings != null && ExcludeBindings.Count > 0)
+ {
+ input.Show("ExcludeBindings", string.Join(",", ExcludeBindings), level: 1);
+ }
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptionsFactory.cs b/src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptionsFactory.cs
new file mode 100644
index 0000000..90a336e
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/IISSites/IISSitesOptionsFactory.cs
@@ -0,0 +1,133 @@
+using PKISharp.WACS.Clients.IIS;
+using PKISharp.WACS.Extensions;
+using PKISharp.WACS.Plugins.Base.Factories;
+using PKISharp.WACS.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class IISSitesOptionsFactory : TargetPluginOptionsFactory<IISSites, IISSitesOptions>
+ {
+ public override bool Hidden => !_iisClient.HasWebSites;
+ protected IIISClient _iisClient;
+ protected IISSiteHelper _siteHelper;
+ protected IISSiteOptionsHelper _optionsHelper;
+ private ILogService _log;
+ private IArgumentsService _arguments;
+
+ public IISSitesOptionsFactory(ILogService log, IIISClient iisClient,
+ IISSiteHelper helper, IArgumentsService arguments)
+ {
+ _iisClient = iisClient;
+ _siteHelper = helper;
+ _log = log;
+ _arguments = arguments;
+ _optionsHelper = new IISSiteOptionsHelper(log);
+ }
+
+ public override IISSitesOptions Aquire(IInputService input, RunLevel runLevel)
+ {
+ var ret = new IISSitesOptions();
+ var sites = _siteHelper.GetSites(_arguments.MainArguments.HideHttps, true).
+ Where(x => x.Hidden == false).
+ Where(x => x.Hosts.Any()).
+ ToList();
+ if (!sites.Any())
+ {
+ _log.Error($"No sites with named bindings have been configured in IIS. Add one or choose '{ManualOptions.DescriptionText}'.");
+ return null;
+ }
+ input.WritePagedList(sites.Select(x => Choice.Create(x, $"{x.Name} ({x.Hosts.Count()} bindings)", x.Id.ToString())).ToList());
+ var sanInput = input.RequestString("Enter a comma separated list of SiteIds or 'S' for all sites");
+ sites = ProcessSiteIds(ret, sites, sanInput);
+ if (sites == null)
+ {
+ return null;
+ }
+ var hosts = sites.SelectMany(x => x.Hosts).Distinct().OrderBy(x => x);
+ if (_optionsHelper.AquireAdvancedOptions(input, hosts, runLevel, ret))
+ {
+ return ret;
+ }
+ return ret;
+ }
+
+ public override IISSitesOptions Default()
+ {
+ var ret = new IISSitesOptions();
+ var args = _arguments.GetArguments<IISSiteArguments>();
+ var sites = _siteHelper.GetSites(false, false);
+ var rawSiteIds = _arguments.TryGetRequiredArgument(nameof(args.SiteId), args.SiteId);
+ sites = ProcessSiteIds(ret, sites, rawSiteIds);
+ if (sites == null)
+ {
+ return null;
+ }
+ if (_optionsHelper.DefaultAdvancedOptions(args, sites.SelectMany(s => s.Hosts), RunLevel.Unattended, ret))
+ {
+ return ret;
+ }
+ return null;
+ }
+
+ private List<IISSiteHelper.IISSiteOption> ProcessSiteIds(IISSitesOptions options, List<IISSiteHelper.IISSiteOption> sites, string sanInput)
+ {
+ if (string.Equals(sanInput, "s", StringComparison.InvariantCultureIgnoreCase))
+ {
+ options.All = true;
+ }
+ else
+ {
+ sites = FilterOptions(sites, sanInput);
+ if (sites == null)
+ {
+ return null;
+ }
+ options.SiteIds = sites.Select(x => x.Id).OrderBy(x => x).ToList();
+ }
+ return sites;
+ }
+
+ private List<IISSiteHelper.IISSiteOption> FilterOptions(List<IISSiteHelper.IISSiteOption> targets, string sanInput)
+ {
+ var siteList = new List<IISSiteHelper.IISSiteOption>();
+ if (string.Equals(sanInput, "s", StringComparison.InvariantCultureIgnoreCase))
+ {
+ return targets;
+ }
+ else
+ {
+ var siteIDs = sanInput.ParseCsv();
+ foreach (var idString in siteIDs)
+ {
+ if (int.TryParse(idString, out var id))
+ {
+ var site = targets.Where(t => t.Id == id).FirstOrDefault();
+ if (site != null)
+ {
+ siteList.Add(site);
+ }
+ else
+ {
+ _log.Error($"SiteId '{idString}' not found");
+ return null;
+ }
+ }
+ else
+ {
+ _log.Error($"Invalid SiteId '{idString}', should be a number");
+ return null;
+ }
+ }
+ if (siteList.Count == 0)
+ {
+ _log.Warning($"No valid sites selected");
+ return null;
+ }
+ }
+ return siteList;
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/Manual/Manual.cs b/src/main.lib/Plugins/TargetPlugins/Manual/Manual.cs
new file mode 100644
index 0000000..c6cb542
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/Manual/Manual.cs
@@ -0,0 +1,33 @@
+using PKISharp.WACS.DomainObjects;
+using PKISharp.WACS.Plugins.Interfaces;
+using PKISharp.WACS.Services;
+using System.Collections.Generic;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class Manual : ITargetPlugin
+ {
+ private ILogService _log;
+ private ManualOptions _options;
+
+ public Manual(ILogService logService, ManualOptions options)
+ {
+ _log = logService;
+ _options = options;
+ }
+
+ public Target Generate()
+ {
+ return new Target()
+ {
+ FriendlyName = $"[{nameof(Manual)}] {_options.CommonName}",
+ CommonName = _options.CommonName,
+ Parts = new List<TargetPart> {
+ new TargetPart {
+ Identifiers = _options.AlternativeNames
+ }
+ }
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main.lib/Plugins/TargetPlugins/Manual/ManualArguments.cs b/src/main.lib/Plugins/TargetPlugins/Manual/ManualArguments.cs
new file mode 100644
index 0000000..18cae37
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/Manual/ManualArguments.cs
@@ -0,0 +1,8 @@
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class ManualArguments
+ {
+ public string CommonName { get; set; }
+ public string Host { get; set; }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/Manual/ManualArgumentsProvider.cs b/src/main.lib/Plugins/TargetPlugins/Manual/ManualArgumentsProvider.cs
new file mode 100644
index 0000000..34a76e1
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/Manual/ManualArgumentsProvider.cs
@@ -0,0 +1,27 @@
+using Fclp;
+using PKISharp.WACS.Configuration;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ class ManualArgumentsProvider : BaseArgumentsProvider<ManualArguments>
+ {
+ public override string Name => "Manual plugin";
+ public override string Group => "Target";
+ public override string Condition => "--target manual";
+
+ public override bool Active(ManualArguments current)
+ {
+ return !string.IsNullOrEmpty(current.Host);
+ }
+
+ public override void Configure(FluentCommandLineParser<ManualArguments> parser)
+ {
+ parser.Setup(o => o.CommonName)
+ .As("commonname")
+ .WithDescription("Specify the common name of the certificate. If not provided the first host name will be used.");
+ parser.Setup(o => o.Host)
+ .As("host")
+ .WithDescription("A host name to get a certificate for. This may be a comma separated list.");
+ }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/Manual/ManualOptions.cs b/src/main.lib/Plugins/TargetPlugins/Manual/ManualOptions.cs
new file mode 100644
index 0000000..e99a6a0
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/Manual/ManualOptions.cs
@@ -0,0 +1,18 @@
+using PKISharp.WACS.Plugins.Base;
+using PKISharp.WACS.Plugins.Base.Options;
+using System.Collections.Generic;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ [Plugin("e239db3b-b42f-48aa-b64f-46d4f3e9941b")]
+ class ManualOptions : TargetPluginOptions<Manual>
+ {
+ public static string DescriptionText = "Manual input";
+
+ public override string Name => "Manual";
+ public override string Description => DescriptionText;
+
+ public string CommonName { get; set; }
+ public List<string> AlternativeNames { get; set; }
+ }
+}
diff --git a/src/main.lib/Plugins/TargetPlugins/Manual/ManualOptionsFactory.cs b/src/main.lib/Plugins/TargetPlugins/Manual/ManualOptionsFactory.cs
new file mode 100644
index 0000000..3a4c3a7
--- /dev/null
+++ b/src/main.lib/Plugins/TargetPlugins/Manual/ManualOptionsFactory.cs
@@ -0,0 +1,64 @@
+using PKISharp.WACS.Extensions;
+using PKISharp.WACS.Plugins.Base.Factories;
+using PKISharp.WACS.Services;
+using System.Linq;
+
+namespace PKISharp.WACS.Plugins.TargetPlugins
+{
+ internal class ManualOptionsFactory : TargetPluginOptionsFactory<Manual, ManualOptions>
+ {
+ private readonly IArgumentsService _arguments;
+ public ManualOptionsFactory(IArgumentsService arguments)
+ {
+ _arguments = arguments;
+ }
+
+ public override ManualOptions Aquire(IInputService inputService, RunLevel runLevel)
+ {
+ var input = inputService.RequestString("Enter comma-separated list of host names, starting with the common name");
+ if (string.IsNullOrEmpty(input))
+ {
+ return null;
+ }
+ else
+ {
+ return Create(input);
+ }
+ }
+
+ public override ManualOptions Default()
+ {
+ var args = _arguments.GetArguments<ManualArguments>();
+ var input = _arguments.TryGetRequiredArgument(nameof(args.Host), args.Host);
+ var ret = Create(input);
+ var commonName = args.CommonName;
+ if (!string.IsNullOrWhiteSpace(commonName))
+ {
+ commonName = commonName.ToLower().Trim().ConvertPunycode();
+ ret.CommonName = commonName;
+ if (!ret.AlternativeNames.Contains(commonName))
+ {
+ ret.AlternativeNames.Insert(0, commonName);
+ }
+ }
+ return ret;
+ }
+
+ private ManualOptions Create(string input)
+ {
+ var sanList = input.ParseCsv().Select(x => x.ConvertPunycode());
+ if (sanList != null)
+ {
+ return new ManualOptions()
+ {
+ CommonName = sanList.First(),
+ AlternativeNames = sanList.ToList()
+ };
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}