Skip to content

Commit

Permalink
gandiv5: add Personal Access Token support (#2007)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez authored Sep 20, 2023
1 parent 766e581 commit 113648a
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 26 deletions.
36 changes: 20 additions & 16 deletions providers/dns/gandiv5/gandiv5.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -23,7 +24,8 @@ const minTTL = 300
const (
envNamespace = "GANDIV5_"

EnvAPIKey = envNamespace + "API_KEY"
EnvAPIKey = envNamespace + "API_KEY"
EnvPersonalAccessToken = envNamespace + "PERSONAL_ACCESS_TOKEN"

EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
Expand All @@ -39,12 +41,13 @@ type inProgressInfo struct {

// Config is used to configure the creation of the DNSProvider.
type Config struct {
BaseURL string
APIKey string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
HTTPClient *http.Client
BaseURL string
APIKey string // Deprecated use PersonalAccessToken
PersonalAccessToken string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
HTTPClient *http.Client
}

// NewDefaultConfig returns a default configuration for the DNSProvider.
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions providers/dns/gandiv5/gandiv5.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
13 changes: 6 additions & 7 deletions providers/dns/gandiv5/gandiv5_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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",
},
}

Expand Down Expand Up @@ -70,7 +69,7 @@ func TestNewDNSProviderConfig(t *testing.T) {
},
{
desc: "missing credentials",
expected: "gandiv5: no API Key given",
expected: "gandiv5: credentials information are missing",
},
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down
11 changes: 10 additions & 1 deletion providers/dns/gandiv5/internal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
}
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 113648a

Please sign in to comment.