From b035c8ad11aa17afb656fd0e7fe0bccab019415c Mon Sep 17 00:00:00 2001 From: Bastien HUBERT Date: Thu, 30 Nov 2023 10:12:12 +0100 Subject: [PATCH] Migrate GandiProvider to new LiveDns api (#639) --- KeyVault.Acmebot/Options/AcmebotOptions.cs | 2 + .../Options/GandiLiveDnsOptions.cs | 7 + .../Providers/GandiLiveDnsProvider.cs | 156 ++++++++++++++++++ KeyVault.Acmebot/Startup.cs | 1 + 4 files changed, 166 insertions(+) create mode 100644 KeyVault.Acmebot/Options/GandiLiveDnsOptions.cs create mode 100644 KeyVault.Acmebot/Providers/GandiLiveDnsProvider.cs diff --git a/KeyVault.Acmebot/Options/AcmebotOptions.cs b/KeyVault.Acmebot/Options/AcmebotOptions.cs index bfae8ef1..4355e425 100644 --- a/KeyVault.Acmebot/Options/AcmebotOptions.cs +++ b/KeyVault.Acmebot/Options/AcmebotOptions.cs @@ -43,6 +43,8 @@ public class AcmebotOptions public GandiOptions Gandi { get; set; } + public GandiLiveDnsOptions GandiLiveDns { get; set; } + public GoDaddyOptions GoDaddy { get; set; } public GoogleDnsOptions GoogleDns { get; set; } diff --git a/KeyVault.Acmebot/Options/GandiLiveDnsOptions.cs b/KeyVault.Acmebot/Options/GandiLiveDnsOptions.cs new file mode 100644 index 00000000..0ec37a65 --- /dev/null +++ b/KeyVault.Acmebot/Options/GandiLiveDnsOptions.cs @@ -0,0 +1,7 @@ +namespace KeyVault.Acmebot.Options; + +public class GandiLiveDnsOptions +{ + public string ApiKey { get; set; } + +} diff --git a/KeyVault.Acmebot/Providers/GandiLiveDnsProvider.cs b/KeyVault.Acmebot/Providers/GandiLiveDnsProvider.cs new file mode 100644 index 00000000..922ad8bc --- /dev/null +++ b/KeyVault.Acmebot/Providers/GandiLiveDnsProvider.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + +using KeyVault.Acmebot.Internal; +using KeyVault.Acmebot.Options; + +using Newtonsoft.Json; + +namespace KeyVault.Acmebot.Providers; + +public class GandiLiveDnsProvider : IDnsProvider +{ + public GandiLiveDnsProvider(GandiLiveDnsOptions options) + { + _client = new GandiLiveDnsClient(options.ApiKey); + } + + private readonly GandiLiveDnsClient _client; + + public string Name => "Gandi LiveDNS"; + + public int PropagationSeconds => 300; + + public async Task> ListZonesAsync() + { + var zones = await _client.ListZonesAsync(); + + return zones.Select(x => new DnsZone(this) { Id = x.Fqdn, Name = x.FqdnUnicode }).ToArray(); + } + + public Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable values) + { + return _client.AddRecordAsync(zone.Name, relativeRecordName, values); + } + + public Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName) + { + return _client.DeleteRecordAsync(zone.Name, relativeRecordName); + } + + private class GandiLiveDnsClient + { + public GandiLiveDnsClient(string apiKey) + { + ArgumentNullException.ThrowIfNull(apiKey); + + _httpClient = new HttpClient + { + BaseAddress = new Uri("https://api.gandi.net/v5/") + }; + + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + apiKey); + } + + private readonly HttpClient _httpClient; + + public async Task> ListZonesAsync() + { + var response = await _httpClient.GetAsync("domain/domains"); + + response.EnsureSuccessStatusCode(); + var domains = await response.Content.ReadAsAsync(); + + return domains.Where(x => x.Nameserver.Current == "livedns").ToArray(); + } + + public async Task DeleteRecordAsync(string zoneName, string relativeRecordName) + { + var response = await _httpClient.DeleteAsync($"livedns/domains/{zoneName}/records/{relativeRecordName}/TXT"); + + if (response.StatusCode != HttpStatusCode.NotFound) + { + response.EnsureSuccessStatusCode(); + } + } + + public async Task AddRecordAsync(string zoneName, string relativeRecordName, IEnumerable values) + { + var response = await _httpClient.PostAsync($"livedns/domains/{zoneName}/records/{relativeRecordName}/TXT", new + { + rrset_values = values.ToArray(), + rrset_ttl = 300 //300 is the minimal value + }); + + response.EnsureSuccessStatusCode(); + } + } + public class Domain + { + [JsonProperty("fqdn")] + public string Fqdn { get; set; } + + [JsonProperty("tld")] + public string Tld { get; set; } + + [JsonProperty("status")] + public List Status { get; set; } + + [JsonProperty("dates")] + public Dates Dates { get; set; } + + [JsonProperty("nameserver")] + public Nameserver Nameserver { get; set; } + + [JsonProperty("autorenew")] + public bool Autorenew { get; set; } + + [JsonProperty("domain_owner")] + public string DomainOwner { get; set; } + + [JsonProperty("orga_owner")] + public string OrgaOwner { get; set; } + + [JsonProperty("owner")] + public string Owner { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("tags")] + public List Tags { get; set; } + + [JsonProperty("href")] + public string Href { get; set; } + + [JsonProperty("fqdn_unicode")] + public string FqdnUnicode { get; set; } + } + + public class Dates + { + [JsonProperty("created_at")] + public DateTime CreatedAt { get; set; } + + [JsonProperty("registry_created_at")] + public DateTime RegistryCreatedAt { get; set; } + + [JsonProperty("registry_ends_at")] + public DateTime RegistryEndsAt { get; set; } + + [JsonProperty("updated_at")] + public DateTime UpdatedAt { get; set; } + } + + public class Nameserver + { + [JsonProperty("current")] + public string Current { get; set; } + } +} diff --git a/KeyVault.Acmebot/Startup.cs b/KeyVault.Acmebot/Startup.cs index f531ce90..73b78425 100644 --- a/KeyVault.Acmebot/Startup.cs +++ b/KeyVault.Acmebot/Startup.cs @@ -120,6 +120,7 @@ public override void Configure(IFunctionsHostBuilder builder) dnsProviders.TryAdd(options.CustomDns, o => new CustomDnsProvider(o)); dnsProviders.TryAdd(options.DnsMadeEasy, o => new DnsMadeEasyProvider(o)); dnsProviders.TryAdd(options.Gandi, o => new GandiProvider(o)); + dnsProviders.TryAdd(options.GandiLiveDns, o => new GandiLiveDnsProvider(o)); dnsProviders.TryAdd(options.GoDaddy, o => new GoDaddyProvider(o)); dnsProviders.TryAdd(options.GoogleDns, o => new GoogleDnsProvider(o)); dnsProviders.TryAdd(options.Route53, o => new Route53Provider(o));