Skip to content

Commit a6b2b5f

Browse files
author
Bastien Hubert
committed
Add support for new Gandi LiveDns Api
1 parent 5f379a6 commit a6b2b5f

File tree

4 files changed

+195
-35
lines changed

4 files changed

+195
-35
lines changed
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
namespace KeyVault.Acmebot.Options;
1+
using System;
2+
3+
namespace KeyVault.Acmebot.Options;
24

35
public class GandiOptions
46
{
57
public string ApiKey { get; set; }
8+
9+
public Boolean UseLiveDnsApi { get; set; } = false;
610
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Net.Http;
6+
using System.Net.Http.Headers;
7+
using System.Threading.Tasks;
8+
9+
using KeyVault.Acmebot.Internal;
10+
using KeyVault.Acmebot.Options;
11+
12+
using Newtonsoft.Json;
13+
14+
namespace KeyVault.Acmebot.Providers;
15+
16+
public class GandiLegacyProvider : IDnsProvider
17+
{
18+
public GandiLegacyProvider(GandiOptions options)
19+
{
20+
_client = new GandiClient(options.ApiKey);
21+
}
22+
23+
private readonly GandiClient _client;
24+
25+
public string Name => "Gandi Legacy";
26+
27+
public int PropagationSeconds => 300;
28+
29+
public async Task<IReadOnlyList<DnsZone>> ListZonesAsync()
30+
{
31+
var zones = await _client.ListZonesAsync();
32+
33+
// Do NOT include the PrimaryNameServer element from the DnsZone list for now,
34+
// the return value from Gandi when returning zones is not the expected value when doing the intersect at the Dns01Precondition method
35+
36+
return zones.Select(x => new DnsZone(this) { Id = x.Uuid, Name = x.Name }).ToArray();
37+
}
38+
39+
public Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable<string> values)
40+
{
41+
return _client.AddRecordAsync(zone.Name, relativeRecordName, values);
42+
}
43+
44+
public Task DeleteTxtRecordAsync(DnsZone zone, string relativeRecordName)
45+
{
46+
return _client.DeleteRecordAsync(zone.Name, relativeRecordName);
47+
}
48+
49+
private class GandiClient
50+
{
51+
public GandiClient(string apiKey)
52+
{
53+
ArgumentNullException.ThrowIfNull(apiKey);
54+
55+
_httpClient = new HttpClient
56+
{
57+
BaseAddress = new Uri("https://dns.api.gandi.net/api/v5/")
58+
};
59+
60+
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
61+
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Api-Key", apiKey);
62+
}
63+
64+
private readonly HttpClient _httpClient;
65+
66+
public async Task<IReadOnlyList<Zone>> ListZonesAsync()
67+
{
68+
var response = await _httpClient.GetAsync("zones");
69+
70+
response.EnsureSuccessStatusCode();
71+
72+
return await response.Content.ReadAsAsync<Zone[]>();
73+
}
74+
75+
public async Task DeleteRecordAsync(string zoneName, string relativeRecordName)
76+
{
77+
var response = await _httpClient.DeleteAsync($"domains/{zoneName}/records/{relativeRecordName}/TXT");
78+
79+
if (response.StatusCode != HttpStatusCode.NotFound)
80+
{
81+
response.EnsureSuccessStatusCode();
82+
}
83+
}
84+
85+
public async Task AddRecordAsync(string zoneName, string relativeRecordName, IEnumerable<string> values)
86+
{
87+
var response = await _httpClient.PostAsync($"domains/{zoneName}/records/{relativeRecordName}/TXT", new
88+
{
89+
rrset_values = values.ToArray(),
90+
rrset_ttl = 300 //300 is the minimal value
91+
});
92+
93+
response.EnsureSuccessStatusCode();
94+
}
95+
}
96+
97+
public class Zone
98+
{
99+
[JsonProperty("uuid")]
100+
public string Uuid { get; set; }
101+
102+
[JsonProperty("name")]
103+
public string Name { get; set; }
104+
105+
[JsonProperty("primary_ns")]
106+
public string PrimaryNameServer { get; set; }
107+
108+
[JsonProperty("email")]
109+
public string Email { get; set; }
110+
111+
[JsonProperty("serial")]
112+
public int Serial { get; set; }
113+
114+
[JsonProperty("user_uuid")]
115+
public string UserUuid { get; set; }
116+
117+
[JsonProperty("refresh")]
118+
public int Refresh { get; set; }
119+
120+
[JsonProperty("minimum")]
121+
public int Minimum { get; set; }
122+
123+
[JsonProperty("expire")]
124+
public int Expire { get; set; }
125+
126+
[JsonProperty("retry")]
127+
public int Retry { get; set; }
128+
}
129+
}

KeyVault.Acmebot/Providers/GandiProvider.cs

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ public async Task<IReadOnlyList<DnsZone>> ListZonesAsync()
3030
{
3131
var zones = await _client.ListZonesAsync();
3232

33-
// Do NOT include the PrimaryNameServer element from the DnsZone list for now,
34-
// the return value from Gandi when returning zones is not the expected value when doing the intersect at the Dns01Precondition method
35-
36-
return zones.Select(x => new DnsZone(this) { Id = x.Uuid, Name = x.Name }).ToArray();
33+
return zones.Select(x => new DnsZone(this) { Id = x.Fqdn, Name = x.FqdnUnicode }).ToArray();
3734
}
3835

3936
public Task CreateTxtRecordAsync(DnsZone zone, string relativeRecordName, IEnumerable<string> values)
@@ -54,27 +51,28 @@ public GandiClient(string apiKey)
5451

5552
_httpClient = new HttpClient
5653
{
57-
BaseAddress = new Uri("https://dns.api.gandi.net/api/v5/")
54+
BaseAddress = new Uri("https://api.gandi.net/v5/")
5855
};
5956

6057
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
61-
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Api-Key", apiKey);
58+
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Bearer " + apiKey);
6259
}
6360

6461
private readonly HttpClient _httpClient;
6562

66-
public async Task<IReadOnlyList<Zone>> ListZonesAsync()
63+
public async Task<IReadOnlyList<Domain>> ListZonesAsync()
6764
{
68-
var response = await _httpClient.GetAsync("zones");
65+
var response = await _httpClient.GetAsync("domain/domains");
6966

7067
response.EnsureSuccessStatusCode();
68+
var domains = await response.Content.ReadAsAsync<Domain[]>();
7169

72-
return await response.Content.ReadAsAsync<Zone[]>();
70+
return domains.Where(x => x.Nameserver.Current == "livedns").ToArray();
7371
}
7472

7573
public async Task DeleteRecordAsync(string zoneName, string relativeRecordName)
7674
{
77-
var response = await _httpClient.DeleteAsync($"domains/{zoneName}/records/{relativeRecordName}/TXT");
75+
var response = await _httpClient.DeleteAsync($"livedns/domains/{zoneName}/records/{relativeRecordName}/TXT");
7876

7977
if (response.StatusCode != HttpStatusCode.NotFound)
8078
{
@@ -84,7 +82,7 @@ public async Task DeleteRecordAsync(string zoneName, string relativeRecordName)
8482

8583
public async Task AddRecordAsync(string zoneName, string relativeRecordName, IEnumerable<string> values)
8684
{
87-
var response = await _httpClient.PostAsync($"domains/{zoneName}/records/{relativeRecordName}/TXT", new
85+
var response = await _httpClient.PostAsync($"livedns/domains/{zoneName}/records/{relativeRecordName}/TXT", new
8886
{
8987
rrset_values = values.ToArray(),
9088
rrset_ttl = 300 //300 is the minimal value
@@ -93,37 +91,66 @@ public async Task AddRecordAsync(string zoneName, string relativeRecordName, IEn
9391
response.EnsureSuccessStatusCode();
9492
}
9593
}
96-
97-
public class Zone
94+
public class Domain
9895
{
99-
[JsonProperty("uuid")]
100-
public string Uuid { get; set; }
96+
[JsonProperty("fqdn")]
97+
public string Fqdn { get; set; }
98+
99+
[JsonProperty("tld")]
100+
public string Tld { get; set; }
101+
102+
[JsonProperty("status")]
103+
public List<string> Status { get; set; }
104+
105+
[JsonProperty("dates")]
106+
public Dates Dates { get; set; }
107+
108+
[JsonProperty("nameserver")]
109+
public Nameserver Nameserver { get; set; }
110+
111+
[JsonProperty("autorenew")]
112+
public bool Autorenew { get; set; }
101113

102-
[JsonProperty("name")]
103-
public string Name { get; set; }
114+
[JsonProperty("domain_owner")]
115+
public string DomainOwner { get; set; }
104116

105-
[JsonProperty("primary_ns")]
106-
public string PrimaryNameServer { get; set; }
117+
[JsonProperty("orga_owner")]
118+
public string OrgaOwner { get; set; }
107119

108-
[JsonProperty("email")]
109-
public string Email { get; set; }
120+
[JsonProperty("owner")]
121+
public string Owner { get; set; }
110122

111-
[JsonProperty("serial")]
112-
public int Serial { get; set; }
123+
[JsonProperty("id")]
124+
public string Id { get; set; }
113125

114-
[JsonProperty("user_uuid")]
115-
public string UserUuid { get; set; }
126+
[JsonProperty("tags")]
127+
public List<string> Tags { get; set; }
116128

117-
[JsonProperty("refresh")]
118-
public int Refresh { get; set; }
129+
[JsonProperty("href")]
130+
public string Href { get; set; }
119131

120-
[JsonProperty("minimum")]
121-
public int Minimum { get; set; }
132+
[JsonProperty("fqdn_unicode")]
133+
public string FqdnUnicode { get; set; }
134+
}
135+
136+
public class Dates
137+
{
138+
[JsonProperty("created_at")]
139+
public DateTime CreatedAt { get; set; }
140+
141+
[JsonProperty("registry_created_at")]
142+
public DateTime RegistryCreatedAt { get; set; }
122143

123-
[JsonProperty("expire")]
124-
public int Expire { get; set; }
144+
[JsonProperty("registry_ends_at")]
145+
public DateTime RegistryEndsAt { get; set; }
125146

126-
[JsonProperty("retry")]
127-
public int Retry { get; set; }
147+
[JsonProperty("updated_at")]
148+
public DateTime UpdatedAt { get; set; }
149+
}
150+
151+
public class Nameserver
152+
{
153+
[JsonProperty("current")]
154+
public string Current { get; set; }
128155
}
129156
}

KeyVault.Acmebot/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public override void Configure(IFunctionsHostBuilder builder)
119119
dnsProviders.TryAdd(options.Cloudflare, o => new CloudflareProvider(o));
120120
dnsProviders.TryAdd(options.CustomDns, o => new CustomDnsProvider(o));
121121
dnsProviders.TryAdd(options.DnsMadeEasy, o => new DnsMadeEasyProvider(o));
122-
dnsProviders.TryAdd(options.Gandi, o => new GandiProvider(o));
122+
dnsProviders.TryAdd(options.Gandi, o => o.UseLiveDnsApi ? new GandiProvider(o) : new GandiLegacyProvider(o));
123123
dnsProviders.TryAdd(options.GoDaddy, o => new GoDaddyProvider(o));
124124
dnsProviders.TryAdd(options.GoogleDns, o => new GoogleDnsProvider(o));
125125
dnsProviders.TryAdd(options.Route53, o => new Route53Provider(o));

0 commit comments

Comments
 (0)