gandiv5: add Personal Access Token support (#2007)

This commit is contained in:
Ludovic Fernandez 2023-09-20 05:42:25 +02:00 committed by GitHub
parent 766e581f8d
commit 113648a368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 26 deletions

View file

@ -11,6 +11,7 @@ import (
"time"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/platform/config/env"
"github.com/go-acme/lego/v4/providers/dns/gandiv5/internal"
)
@ -24,6 +25,7 @@ const (
envNamespace = "GANDIV5_"
EnvAPIKey = envNamespace + "API_KEY"
EnvPersonalAccessToken = envNamespace + "PERSONAL_ACCESS_TOKEN"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
@ -40,7 +42,8 @@ type inProgressInfo struct {
// Config is used to configure the creation of the DNSProvider.
type Config struct {
BaseURL string
APIKey string
APIKey string // Deprecated use PersonalAccessToken
PersonalAccessToken string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
@ -76,13 +79,10 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for Gandi.
// Credentials must be passed in the environment variable: GANDIV5_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvAPIKey)
if err != nil {
return nil, fmt.Errorf("gandi: %w", err)
}
// TODO(ldez): rewrite this when APIKey will be removed.
config := NewDefaultConfig()
config.APIKey = values[EnvAPIKey]
config.APIKey = env.GetOrFile(EnvAPIKey)
config.PersonalAccessToken = env.GetOrFile(EnvPersonalAccessToken)
return NewDNSProviderConfig(config)
}
@ -93,15 +93,19 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("gandiv5: the configuration of the DNS provider is nil")
}
if config.APIKey == "" {
return nil, errors.New("gandiv5: no API Key given")
if config.APIKey != "" {
log.Print("gandiv5: API Key is deprecated, use Personal Access Token instead")
}
if config.APIKey == "" && config.PersonalAccessToken == "" {
return nil, errors.New("gandiv5: credentials information are missing")
}
if config.TTL < minTTL {
return nil, fmt.Errorf("gandiv5: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL)
}
client := internal.NewClient(config.APIKey)
client := internal.NewClient(config.APIKey, config.PersonalAccessToken)
if config.BaseURL != "" {
baseURL, err := url.Parse(config.BaseURL)

View file

@ -5,13 +5,14 @@ Code = "gandiv5"
Since = "v0.5.0"
Example = '''
GANDIV5_API_KEY=abcdefghijklmnopqrstuvwx \
GANDIV5_PERSONAL_ACCESS_TOKEN=abcdefghijklmnopqrstuvwx \
lego --email you@example.com --dns gandiv5 --domains my.example.org run
'''
[Configuration]
[Configuration.Credentials]
GANDIV5_API_KEY = "API key"
GANDIV5_PERSONAL_ACCESS_TOKEN = "Personal Access Token"
GANDIV5_API_KEY = "API key (Deprecated)"
[Configuration.Additional]
GANDIV5_POLLING_INTERVAL = "Time between DNS propagation check"
GANDIV5_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"

View file

@ -10,11 +10,10 @@ import (
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/platform/tester"
"github.com/go-acme/lego/v4/providers/dns/gandiv5/internal"
"github.com/stretchr/testify/require"
)
var envTest = tester.NewEnvTest(EnvAPIKey)
var envTest = tester.NewEnvTest(EnvAPIKey, EnvPersonalAccessToken)
func TestNewDNSProvider(t *testing.T) {
testCases := []struct {
@ -33,7 +32,7 @@ func TestNewDNSProvider(t *testing.T) {
envVars: map[string]string{
EnvAPIKey: "",
},
expected: "gandi: some credentials information are missing: GANDIV5_API_KEY",
expected: "gandiv5: credentials information are missing",
},
}
@ -70,7 +69,7 @@ func TestNewDNSProviderConfig(t *testing.T) {
},
{
desc: "missing credentials",
expected: "gandiv5: no API Key given",
expected: "gandiv5: credentials information are missing",
},
}
@ -122,8 +121,8 @@ func TestDNSProvider(t *testing.T) {
mux.HandleFunc("/domains/example.com/records/_acme-challenge.abc.def/TXT", func(rw http.ResponseWriter, req *http.Request) {
log.Infof("request: %s %s", req.Method, req.URL)
if req.Header.Get(internal.APIKeyHeader) == "" {
http.Error(rw, `{"message": "missing API key"}`, http.StatusUnauthorized)
if req.Header.Get("Authorization") == "" {
http.Error(rw, `{"message": "missing Authorization"}`, http.StatusUnauthorized)
return
}
@ -165,7 +164,7 @@ func TestDNSProvider(t *testing.T) {
}
config := NewDefaultConfig()
config.APIKey = "123412341234123412341234"
config.PersonalAccessToken = "123412341234123412341234"
config.BaseURL = server.URL
provider, err := NewDNSProviderConfig(config)

View file

@ -20,20 +20,25 @@ const defaultBaseURL = "https://dns.api.gandi.net/api/v5"
// APIKeyHeader API key header.
const APIKeyHeader = "X-Api-Key"
// Related to Personal Access Token.
const authorizationHeader = "Authorization"
// Client the Gandi API v5 client.
type Client struct {
apiKey string
pat string
BaseURL *url.URL
HTTPClient *http.Client
}
// NewClient Creates a new Client.
func NewClient(apiKey string) *Client {
func NewClient(apiKey, pat string) *Client {
baseURL, _ := url.Parse(defaultBaseURL)
return &Client{
apiKey: apiKey,
pat: pat,
BaseURL: baseURL,
HTTPClient: &http.Client{Timeout: 5 * time.Second},
}
@ -128,6 +133,10 @@ func (c *Client) do(req *http.Request, result any) error {
req.Header.Set(APIKeyHeader, c.apiKey)
}
if c.pat != "" {
req.Header.Set(authorizationHeader, c.pat)
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return errutils.NewHTTPDoError(req, err)