mirror of
https://github.com/go-acme/lego.git
synced 2024-12-27 03:32:05 +02:00
Add DNS provider for nicmanager (#1473)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
parent
376e7bd78e
commit
d2e526e8dd
16
README.md
16
README.md
@ -62,14 +62,14 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
|
|||||||
| [Joker](https://go-acme.github.io/lego/dns/joker/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/) | [Linode (v4)](https://go-acme.github.io/lego/dns/linode/) | [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) |
|
| [Joker](https://go-acme.github.io/lego/dns/joker/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/) | [Linode (v4)](https://go-acme.github.io/lego/dns/linode/) | [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) |
|
||||||
| [Loopia](https://go-acme.github.io/lego/dns/loopia/) | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/) | [Manual](https://go-acme.github.io/lego/dns/manual/) | [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) |
|
| [Loopia](https://go-acme.github.io/lego/dns/loopia/) | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/) | [Manual](https://go-acme.github.io/lego/dns/manual/) | [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) |
|
||||||
| [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/) | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) | [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) |
|
| [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/) | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) | [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) |
|
||||||
| [Netcup](https://go-acme.github.io/lego/dns/netcup/) | [Netlify](https://go-acme.github.io/lego/dns/netlify/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) | [Njalla](https://go-acme.github.io/lego/dns/njalla/) |
|
| [Netcup](https://go-acme.github.io/lego/dns/netcup/) | [Netlify](https://go-acme.github.io/lego/dns/netlify/) | [Nicmanager](https://go-acme.github.io/lego/dns/nicmanager/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) |
|
||||||
| [NS1](https://go-acme.github.io/lego/dns/ns1/) | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/) | [OVH](https://go-acme.github.io/lego/dns/ovh/) |
|
| [Njalla](https://go-acme.github.io/lego/dns/njalla/) | [NS1](https://go-acme.github.io/lego/dns/ns1/) | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/) |
|
||||||
| [Porkbun](https://go-acme.github.io/lego/dns/porkbun/) | [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) | [reg.ru](https://go-acme.github.io/lego/dns/regru/) |
|
| [OVH](https://go-acme.github.io/lego/dns/ovh/) | [Porkbun](https://go-acme.github.io/lego/dns/porkbun/) | [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) |
|
||||||
| [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) | [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) | [Scaleway](https://go-acme.github.io/lego/dns/scaleway/) |
|
| [reg.ru](https://go-acme.github.io/lego/dns/regru/) | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) | [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) |
|
||||||
| [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Simply.com](https://go-acme.github.io/lego/dns/simply/) | [Sonic](https://go-acme.github.io/lego/dns/sonic/) |
|
| [Scaleway](https://go-acme.github.io/lego/dns/scaleway/) | [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Simply.com](https://go-acme.github.io/lego/dns/simply/) |
|
||||||
| [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) |
|
| [Sonic](https://go-acme.github.io/lego/dns/sonic/) | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) |
|
||||||
| [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [WEDOS](https://go-acme.github.io/lego/dns/wedos/) |
|
| [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) |
|
||||||
| [Yandex](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | |
|
| [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) |
|
||||||
|
|
||||||
<!-- END DNS PROVIDERS LIST -->
|
<!-- END DNS PROVIDERS LIST -->
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ func allDNSCodes() string {
|
|||||||
"namesilo",
|
"namesilo",
|
||||||
"netcup",
|
"netcup",
|
||||||
"netlify",
|
"netlify",
|
||||||
|
"nicmanager",
|
||||||
"nifcloud",
|
"nifcloud",
|
||||||
"njalla",
|
"njalla",
|
||||||
"ns1",
|
"ns1",
|
||||||
@ -1472,6 +1473,31 @@ func displayDNSHelp(name string) error {
|
|||||||
ew.writeln()
|
ew.writeln()
|
||||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/netlify`)
|
ew.writeln(`More information: https://go-acme.github.io/lego/dns/netlify`)
|
||||||
|
|
||||||
|
case "nicmanager":
|
||||||
|
// generated from: providers/dns/nicmanager/nicmanager.toml
|
||||||
|
ew.writeln(`Configuration for Nicmanager.`)
|
||||||
|
ew.writeln(`Code: 'nicmanager'`)
|
||||||
|
ew.writeln(`Since: 'v4.5.0'`)
|
||||||
|
ew.writeln()
|
||||||
|
|
||||||
|
ew.writeln(`Credentials:`)
|
||||||
|
ew.writeln(` - "NICMANAGER_API_EMAIL": Email-based login`)
|
||||||
|
ew.writeln(` - "NICMANAGER_API_LOGIN": Login, used for Username-based login`)
|
||||||
|
ew.writeln(` - "NICMANAGER_API_PASSWORD": Password, always required`)
|
||||||
|
ew.writeln(` - "NICMANAGER_API_USERNAME": Username, used for Username-based login`)
|
||||||
|
ew.writeln()
|
||||||
|
|
||||||
|
ew.writeln(`Additional Configuration:`)
|
||||||
|
ew.writeln(` - "NICMANAGER_API_MODE": mode: 'anycast' or 'zone' (default: 'anycast')`)
|
||||||
|
ew.writeln(` - "NICMANAGER_API_OTP": TOTP Secret (optional)`)
|
||||||
|
ew.writeln(` - "NICMANAGER_HTTP_TIMEOUT": API request timeout`)
|
||||||
|
ew.writeln(` - "NICMANAGER_POLLING_INTERVAL": Time between DNS propagation check`)
|
||||||
|
ew.writeln(` - "NICMANAGER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
|
||||||
|
ew.writeln(` - "NICMANAGER_TTL": The TTL of the TXT record used for the DNS challenge`)
|
||||||
|
|
||||||
|
ew.writeln()
|
||||||
|
ew.writeln(`More information: https://go-acme.github.io/lego/dns/nicmanager`)
|
||||||
|
|
||||||
case "nifcloud":
|
case "nifcloud":
|
||||||
// generated from: providers/dns/nifcloud/nifcloud.toml
|
// generated from: providers/dns/nifcloud/nifcloud.toml
|
||||||
ew.writeln(`Configuration for NIFCloud.`)
|
ew.writeln(`Configuration for NIFCloud.`)
|
||||||
|
89
docs/content/dns/zz_gen_nicmanager.md
Normal file
89
docs/content/dns/zz_gen_nicmanager.md
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
title: "Nicmanager"
|
||||||
|
date: 2019-03-03T16:39:46+01:00
|
||||||
|
draft: false
|
||||||
|
slug: nicmanager
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||||
|
<!-- providers/dns/nicmanager/nicmanager.toml -->
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||||
|
|
||||||
|
Since: v4.5.0
|
||||||
|
|
||||||
|
Configuration for [Nicmanager](https://www.nicmanager.com/).
|
||||||
|
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
- Code: `nicmanager`
|
||||||
|
|
||||||
|
Here is an example bash command using the Nicmanager provider:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
## Login using email
|
||||||
|
|
||||||
|
NICMANAGER_API_EMAIL = "foo@bar.baz" \
|
||||||
|
NICMANAGER_API_PASSWORD = "password" \
|
||||||
|
|
||||||
|
# Optionally, if your account has TOTP enabled, set the secret here
|
||||||
|
NICMANAGER_API_OTP = "long-secret" \
|
||||||
|
|
||||||
|
lego --email myemail@example.com --dns nicmanager --domains my.example.org run
|
||||||
|
|
||||||
|
## Login using account name + username
|
||||||
|
|
||||||
|
NICMANAGER_API_LOGIN = "myaccount" \
|
||||||
|
NICMANAGER_API_USERNAME = "myuser" \
|
||||||
|
NICMANAGER_API_PASSWORD = "password" \
|
||||||
|
|
||||||
|
# Optionally, if your account has TOTP enabled, set the secret here
|
||||||
|
NICMANAGER_API_OTP = "long-secret" \
|
||||||
|
|
||||||
|
lego --email myemail@example.com --dns nicmanager --domains my.example.org run
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
|
||||||
|
| Environment Variable Name | Description |
|
||||||
|
|-----------------------|-------------|
|
||||||
|
| `NICMANAGER_API_EMAIL` | Email-based login |
|
||||||
|
| `NICMANAGER_API_LOGIN` | Login, used for Username-based login |
|
||||||
|
| `NICMANAGER_API_PASSWORD` | Password, always required |
|
||||||
|
| `NICMANAGER_API_USERNAME` | Username, used for Username-based login |
|
||||||
|
|
||||||
|
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||||
|
More information [here](/lego/dns/#configuration-and-credentials).
|
||||||
|
|
||||||
|
|
||||||
|
## Additional Configuration
|
||||||
|
|
||||||
|
| Environment Variable Name | Description |
|
||||||
|
|--------------------------------|-------------|
|
||||||
|
| `NICMANAGER_API_MODE` | mode: 'anycast' or 'zone' (default: 'anycast') |
|
||||||
|
| `NICMANAGER_API_OTP` | TOTP Secret (optional) |
|
||||||
|
| `NICMANAGER_HTTP_TIMEOUT` | API request timeout |
|
||||||
|
| `NICMANAGER_POLLING_INTERVAL` | Time between DNS propagation check |
|
||||||
|
| `NICMANAGER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
|
||||||
|
| `NICMANAGER_TTL` | The TTL of the TXT record used for the DNS challenge |
|
||||||
|
|
||||||
|
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||||
|
More information [here](/lego/dns/#configuration-and-credentials).
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
You can login using your account name + username or using your email address.
|
||||||
|
Optionally if TOTP is configured for your account, set `NICMANAGER_API_OTP`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## More information
|
||||||
|
|
||||||
|
- [API documentation](https://api.nicmanager.com/docs/v1/)
|
||||||
|
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||||
|
<!-- providers/dns/nicmanager/nicmanager.toml -->
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
@ -69,6 +69,7 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/providers/dns/namesilo"
|
"github.com/go-acme/lego/v4/providers/dns/namesilo"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/netcup"
|
"github.com/go-acme/lego/v4/providers/dns/netcup"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/netlify"
|
"github.com/go-acme/lego/v4/providers/dns/netlify"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/nicmanager"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/nifcloud"
|
"github.com/go-acme/lego/v4/providers/dns/nifcloud"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/njalla"
|
"github.com/go-acme/lego/v4/providers/dns/njalla"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/ns1"
|
"github.com/go-acme/lego/v4/providers/dns/ns1"
|
||||||
@ -234,6 +235,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
|
|||||||
return netcup.NewDNSProvider()
|
return netcup.NewDNSProvider()
|
||||||
case "netlify":
|
case "netlify":
|
||||||
return netlify.NewDNSProvider()
|
return netlify.NewDNSProvider()
|
||||||
|
case "nicmanager":
|
||||||
|
return nicmanager.NewDNSProvider()
|
||||||
case "nifcloud":
|
case "nifcloud":
|
||||||
return nifcloud.NewDNSProvider()
|
return nifcloud.NewDNSProvider()
|
||||||
case "njalla":
|
case "njalla":
|
||||||
|
185
providers/dns/nicmanager/internal/client.go
Normal file
185
providers/dns/nicmanager/internal/client.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultBaseURL = "https://api.nicmanager.com/v1"
|
||||||
|
headerTOTPToken = "X-Auth-Token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Modes.
|
||||||
|
const (
|
||||||
|
ModeAnycast = "anycast"
|
||||||
|
ModeZone = "zone"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options the Client options.
|
||||||
|
type Options struct {
|
||||||
|
Login string
|
||||||
|
Username string
|
||||||
|
|
||||||
|
Email string
|
||||||
|
|
||||||
|
Password string
|
||||||
|
OTP string
|
||||||
|
|
||||||
|
Mode string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client a nicmanager DNS client.
|
||||||
|
type Client struct {
|
||||||
|
HTTPClient *http.Client
|
||||||
|
baseURL *url.URL
|
||||||
|
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
otp string
|
||||||
|
|
||||||
|
mode string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient create a new Client.
|
||||||
|
func NewClient(opts Options) *Client {
|
||||||
|
c := &Client{
|
||||||
|
mode: ModeAnycast,
|
||||||
|
username: opts.Email,
|
||||||
|
password: opts.Password,
|
||||||
|
otp: opts.OTP,
|
||||||
|
HTTPClient: &http.Client{Timeout: 10 * time.Second},
|
||||||
|
}
|
||||||
|
|
||||||
|
c.baseURL, _ = url.Parse(defaultBaseURL)
|
||||||
|
|
||||||
|
if opts.Mode != "" {
|
||||||
|
c.mode = opts.Mode
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Login != "" && opts.Username != "" {
|
||||||
|
c.username = fmt.Sprintf("%s.%s", opts.Login, opts.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) GetZone(name string) (*Zone, error) {
|
||||||
|
resp, err := c.do(http.MethodGet, name, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
msg := APIError{StatusCode: resp.StatusCode}
|
||||||
|
if err = json.Unmarshal(b, &msg); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get zone info for %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, msg
|
||||||
|
}
|
||||||
|
|
||||||
|
var zone Zone
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&zone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) AddRecord(zone string, req RecordCreateUpdate) error {
|
||||||
|
resp, err := c.do(http.MethodPost, path.Join(zone, "records"), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusAccepted {
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
msg := APIError{StatusCode: resp.StatusCode}
|
||||||
|
if err = json.Unmarshal(b, &msg); err != nil {
|
||||||
|
return fmt.Errorf("records create should've returned %d but returned %d", http.StatusAccepted, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) DeleteRecord(zone string, record int) error {
|
||||||
|
resp, err := c.do(http.MethodDelete, path.Join(zone, "records", strconv.Itoa(record)), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusAccepted {
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
msg := APIError{StatusCode: resp.StatusCode}
|
||||||
|
if err = json.Unmarshal(b, &msg); err != nil {
|
||||||
|
return fmt.Errorf("records delete should've returned %d but returned %d", http.StatusAccepted, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) do(method, uri string, body interface{}) (*http.Response, error) {
|
||||||
|
var reqBody io.Reader
|
||||||
|
if body != nil {
|
||||||
|
jsonValue, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBody = bytes.NewBuffer(jsonValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint, err := c.baseURL.Parse(path.Join(c.baseURL.Path, c.mode, uri))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := http.NewRequest(method, endpoint.String(), reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Header.Set("Accept", "application/json")
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
r.SetBasicAuth(c.username, c.password)
|
||||||
|
|
||||||
|
if c.otp != "" {
|
||||||
|
tan, err := totp.GenerateCode(c.otp, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Header.Set(headerTOTPToken, tan)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.HTTPClient.Do(r)
|
||||||
|
}
|
145
providers/dns/nicmanager/internal/client_test.go
Normal file
145
providers/dns/nicmanager/internal/client_test.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient_GetZone(t *testing.T) {
|
||||||
|
client := setupTest(t, "/anycast/nicmanager-anycastdns4.net", testHandler(http.MethodGet, http.StatusOK, "zone.json"))
|
||||||
|
|
||||||
|
zone, err := client.GetZone("nicmanager-anycastdns4.net")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := &Zone{
|
||||||
|
Name: "nicmanager-anycastdns4.net",
|
||||||
|
Active: true,
|
||||||
|
Records: []Record{
|
||||||
|
{
|
||||||
|
ID: 186,
|
||||||
|
Name: "nicmanager-anycastdns4.net",
|
||||||
|
Type: "A",
|
||||||
|
Content: "123.123.123.123",
|
||||||
|
TTL: 3600,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expected, zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_GetZone_error(t *testing.T) {
|
||||||
|
client := setupTest(t, "/anycast/foo", testHandler(http.MethodGet, http.StatusNotFound, "error.json"))
|
||||||
|
|
||||||
|
_, err := client.GetZone("foo")
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_AddRecord(t *testing.T) {
|
||||||
|
client := setupTest(t, "/anycast/zonedomain.tld/records", testHandler(http.MethodPost, http.StatusAccepted, "error.json"))
|
||||||
|
|
||||||
|
record := RecordCreateUpdate{
|
||||||
|
Type: "TXT",
|
||||||
|
Name: "lego",
|
||||||
|
Value: "content",
|
||||||
|
TTL: 3600,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.AddRecord("zonedomain.tld", record)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_AddRecord_error(t *testing.T) {
|
||||||
|
client := setupTest(t, "/anycast/zonedomain.tld", testHandler(http.MethodPost, http.StatusUnauthorized, "error.json"))
|
||||||
|
|
||||||
|
record := RecordCreateUpdate{
|
||||||
|
Type: "TXT",
|
||||||
|
Name: "zonedomain.tld",
|
||||||
|
Value: "content",
|
||||||
|
TTL: 3600,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := client.AddRecord("zonedomain.tld", record)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_DeleteRecord(t *testing.T) {
|
||||||
|
client := setupTest(t, "/anycast/zonedomain.tld/records/6", testHandler(http.MethodDelete, http.StatusAccepted, "error.json"))
|
||||||
|
|
||||||
|
err := client.DeleteRecord("zonedomain.tld", 6)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_DeleteRecord_error(t *testing.T) {
|
||||||
|
client := setupTest(t, "/anycast/zonedomain.tld/records/6", testHandler(http.MethodDelete, http.StatusNoContent, ""))
|
||||||
|
|
||||||
|
err := client.DeleteRecord("zonedomain.tld", 7)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTest(t *testing.T, path string, handler http.Handler) *Client {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
t.Cleanup(server.Close)
|
||||||
|
|
||||||
|
mux.Handle(path, handler)
|
||||||
|
|
||||||
|
opts := Options{
|
||||||
|
Login: "foo",
|
||||||
|
Username: "bar",
|
||||||
|
Password: "foo",
|
||||||
|
OTP: "2hsn",
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewClient(opts)
|
||||||
|
client.HTTPClient = server.Client()
|
||||||
|
client.baseURL, _ = url.Parse(server.URL)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandler(method string, statusCode int, filename string) http.HandlerFunc {
|
||||||
|
return func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.Method != method {
|
||||||
|
http.Error(rw, fmt.Sprintf(`{"message":"unsupported method: %s"}`, req.Method), http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, ok := req.BasicAuth()
|
||||||
|
if !ok || username != "foo.bar" || password != "foo" {
|
||||||
|
http.Error(rw, `{"message":"Unauthenticated"}`, http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.WriteHeader(statusCode)
|
||||||
|
|
||||||
|
if statusCode == http.StatusNoContent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(filepath.Join("fixtures", filename))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, fmt.Sprintf(`{"message":"%v"}`, err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
|
_, err = io.Copy(rw, file)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, fmt.Sprintf(`{"message":"%v"}`, err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
providers/dns/nicmanager/internal/fixtures/error.json
Normal file
3
providers/dns/nicmanager/internal/fixtures/error.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"message": "Not Found"
|
||||||
|
}
|
51
providers/dns/nicmanager/internal/fixtures/zone.json
Normal file
51
providers/dns/nicmanager/internal/fixtures/zone.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"order_id": 9053,
|
||||||
|
"name": "nicmanager-anycastdns4.net",
|
||||||
|
"order_status": "active",
|
||||||
|
"event_status": "done",
|
||||||
|
"active": true,
|
||||||
|
"dnssec": "inactive",
|
||||||
|
"master1": null,
|
||||||
|
"master2": null,
|
||||||
|
"soa": {
|
||||||
|
"primary": "ns1.nic53.net",
|
||||||
|
"mail": "hostmaster.nicmanager.de",
|
||||||
|
"serial": 1481109046,
|
||||||
|
"refresh": 14400,
|
||||||
|
"retry": 1800,
|
||||||
|
"expire": 1209600,
|
||||||
|
"default": 3600,
|
||||||
|
"ttl": 86400
|
||||||
|
},
|
||||||
|
"updated_datetime": "2016-09-02T13:52:18Z",
|
||||||
|
"order_datetime": "2016-09-02T13:52:18Z",
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"id": 186,
|
||||||
|
"name": "nicmanager-anycastdns4.net",
|
||||||
|
"type": "A",
|
||||||
|
"content": "123.123.123.123",
|
||||||
|
"ttl": 3600,
|
||||||
|
"priority": 0,
|
||||||
|
"active": true,
|
||||||
|
"updated_datetime": "2016-09-02T13:52:18Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"redirects": [
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "test.nicmanager-anycastdns4.net",
|
||||||
|
"target": "https:\/\/www.nicmanager.com\/",
|
||||||
|
"type": "frame",
|
||||||
|
"updated_datetime": "2016-12-05T14:40:47Z",
|
||||||
|
"request_uri": true,
|
||||||
|
"ssl": false,
|
||||||
|
"meta": {
|
||||||
|
"title": "My frame",
|
||||||
|
"keywords": "foo,bar",
|
||||||
|
"description": "Just a Test"
|
||||||
|
},
|
||||||
|
"subdomain": "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
34
providers/dns/nicmanager/internal/types.go
Normal file
34
providers/dns/nicmanager/internal/types.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Record struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
Type string `json:"type"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Zone struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
Records []Record `json:"records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecordCreateUpdate struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIError struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
StatusCode int `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a APIError) Error() string {
|
||||||
|
return fmt.Sprintf("%d: %s", a.StatusCode, a.Message)
|
||||||
|
}
|
200
providers/dns/nicmanager/nicmanager.go
Normal file
200
providers/dns/nicmanager/nicmanager.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// Package nicmanager implements a DNS provider for solving the DNS-01 challenge using nicmanager DNS.
|
||||||
|
package nicmanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
|
"github.com/go-acme/lego/v4/platform/config/env"
|
||||||
|
"github.com/go-acme/lego/v4/providers/dns/nicmanager/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Environment variables names.
|
||||||
|
const (
|
||||||
|
envNamespace = "NICMANAGER_"
|
||||||
|
|
||||||
|
EnvLogin = envNamespace + "API_LOGIN"
|
||||||
|
EnvUsername = envNamespace + "API_USERNAME"
|
||||||
|
EnvEmail = envNamespace + "API_EMAIL"
|
||||||
|
EnvPassword = envNamespace + "API_PASSWORD"
|
||||||
|
EnvOTP = envNamespace + "API_OTP"
|
||||||
|
EnvMode = envNamespace + "MODE"
|
||||||
|
|
||||||
|
EnvTTL = envNamespace + "TTL"
|
||||||
|
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||||
|
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||||
|
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||||
|
)
|
||||||
|
|
||||||
|
const minTTL = 900
|
||||||
|
|
||||||
|
// Config is used to configure the creation of the DNSProvider.
|
||||||
|
type Config struct {
|
||||||
|
Login string
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
Password string
|
||||||
|
OTPSecret string
|
||||||
|
Mode string
|
||||||
|
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
TTL int
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
TTL: env.GetOrDefaultInt(EnvTTL, minTTL),
|
||||||
|
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 5*time.Minute),
|
||||||
|
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 10*time.Second),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider implements the challenge.Provider interface.
|
||||||
|
type DNSProvider struct {
|
||||||
|
client *internal.Client
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider returns a DNSProvider instance configured for nicmanager.
|
||||||
|
// Credentials must be passed in the environment variables:
|
||||||
|
// NICMANAGER_API_LOGIN, NICMANAGER_API_USERNAME
|
||||||
|
// NICMANAGER_API_EMAIL
|
||||||
|
// NICMANAGER_API_PASSWORD
|
||||||
|
// NICMANAGER_API_OTP
|
||||||
|
// NICMANAGER_API_MODE.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(EnvPassword)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("nicmanager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Password = values[EnvPassword]
|
||||||
|
|
||||||
|
config.Mode = env.GetOrDefaultString(EnvMode, internal.ModeAnycast)
|
||||||
|
config.Username = env.GetOrFile(EnvUsername)
|
||||||
|
config.Login = env.GetOrFile(EnvLogin)
|
||||||
|
config.Email = env.GetOrFile(EnvEmail)
|
||||||
|
config.OTPSecret = env.GetOrFile(EnvOTP)
|
||||||
|
|
||||||
|
if config.TTL < minTTL {
|
||||||
|
return nil, fmt.Errorf("TTL must be higher than %d: %d", minTTL, config.TTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig return a DNSProvider instance configured for nicmanager.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("nicmanager: the configuration of the DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := internal.Options{
|
||||||
|
Password: config.Password,
|
||||||
|
OTP: config.OTPSecret,
|
||||||
|
Mode: config.Mode,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case config.Password == "":
|
||||||
|
return nil, errors.New("nicmanager: credentials missing")
|
||||||
|
case config.Email != "":
|
||||||
|
opts.Email = config.Email
|
||||||
|
case config.Login != "" && config.Username != "":
|
||||||
|
opts.Login = config.Login
|
||||||
|
opts.Username = config.Username
|
||||||
|
default:
|
||||||
|
return nil, errors.New("nicmanager: credentials missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := internal.NewClient(opts)
|
||||||
|
|
||||||
|
if config.HTTPClient != nil {
|
||||||
|
client.HTTPClient = config.HTTPClient
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{client: client, config: config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
// Adjusting here to cope with spikes in propagation times.
|
||||||
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfill the dns-01 challenge.
|
||||||
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||||
|
|
||||||
|
rootDomain, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("nicmanager: could not determine zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, err := d.client.GetZone(dns01.UnFqdn(rootDomain))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("nicmanager: failed to get zone %q: %w", rootDomain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The way nic manager deals with record with multiple values is that they are completely different records with unique ids
|
||||||
|
// Hence we don't check for an existing record here, but rather just create one
|
||||||
|
record := internal.RecordCreateUpdate{
|
||||||
|
Name: fqdn,
|
||||||
|
Type: "TXT",
|
||||||
|
TTL: d.config.TTL,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.client.AddRecord(zone.Name, record)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("nicmanager: failed to create record [zone: %q, fqdn: %q]: %w", zone.Name, fqdn, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||||
|
|
||||||
|
rootDomain, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("nicmanager: could not determine zone for domain %q: %w", domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, err := d.client.GetZone(dns01.UnFqdn(rootDomain))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("nicmanager: failed to get zone %q: %w", rootDomain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := dns01.UnFqdn(fqdn)
|
||||||
|
|
||||||
|
var existingRecord internal.Record
|
||||||
|
var existingRecordFound bool
|
||||||
|
for _, record := range zone.Records {
|
||||||
|
if strings.EqualFold(record.Type, "TXT") && strings.EqualFold(record.Name, name) && record.Content == value {
|
||||||
|
existingRecord = record
|
||||||
|
existingRecordFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingRecordFound {
|
||||||
|
err = d.client.DeleteRecord(zone.Name, existingRecord.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("nicmanager: failed to delete record [zone: %q, domain: %q]: %w", zone.Name, name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("nicmanager: no record found to cleanup")
|
||||||
|
}
|
52
providers/dns/nicmanager/nicmanager.toml
Normal file
52
providers/dns/nicmanager/nicmanager.toml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
Name = "Nicmanager"
|
||||||
|
Description = ''''''
|
||||||
|
URL = "https://www.nicmanager.com/"
|
||||||
|
Code = "nicmanager"
|
||||||
|
Since = "v4.5.0"
|
||||||
|
|
||||||
|
Example = '''
|
||||||
|
## Login using email
|
||||||
|
|
||||||
|
NICMANAGER_API_EMAIL = "foo@bar.baz" \
|
||||||
|
NICMANAGER_API_PASSWORD = "password" \
|
||||||
|
|
||||||
|
# Optionally, if your account has TOTP enabled, set the secret here
|
||||||
|
NICMANAGER_API_OTP = "long-secret" \
|
||||||
|
|
||||||
|
lego --email myemail@example.com --dns nicmanager --domains my.example.org run
|
||||||
|
|
||||||
|
## Login using account name + username
|
||||||
|
|
||||||
|
NICMANAGER_API_LOGIN = "myaccount" \
|
||||||
|
NICMANAGER_API_USERNAME = "myuser" \
|
||||||
|
NICMANAGER_API_PASSWORD = "password" \
|
||||||
|
|
||||||
|
# Optionally, if your account has TOTP enabled, set the secret here
|
||||||
|
NICMANAGER_API_OTP = "long-secret" \
|
||||||
|
|
||||||
|
lego --email myemail@example.com --dns nicmanager --domains my.example.org run
|
||||||
|
'''
|
||||||
|
|
||||||
|
Additional = '''
|
||||||
|
## Description
|
||||||
|
|
||||||
|
You can login using your account name + username or using your email address.
|
||||||
|
Optionally if TOTP is configured for your account, set `NICMANAGER_API_OTP`.
|
||||||
|
'''
|
||||||
|
|
||||||
|
[Configuration]
|
||||||
|
[Configuration.Credentials]
|
||||||
|
NICMANAGER_API_LOGIN = "Login, used for Username-based login"
|
||||||
|
NICMANAGER_API_USERNAME = "Username, used for Username-based login"
|
||||||
|
NICMANAGER_API_EMAIL = "Email-based login"
|
||||||
|
NICMANAGER_API_PASSWORD = "Password, always required"
|
||||||
|
[Configuration.Additional]
|
||||||
|
NICMANAGER_API_OTP = "TOTP Secret (optional)"
|
||||||
|
NICMANAGER_API_MODE = "mode: 'anycast' or 'zone' (default: 'anycast')"
|
||||||
|
NICMANAGER_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||||
|
NICMANAGER_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||||
|
NICMANAGER_TTL = "The TTL of the TXT record used for the DNS challenge"
|
||||||
|
NICMANAGER_HTTP_TIMEOUT = "API request timeout"
|
||||||
|
|
||||||
|
[Links]
|
||||||
|
API = "https://api.nicmanager.com/docs/v1/"
|
182
providers/dns/nicmanager/nicmanager_test.go
Normal file
182
providers/dns/nicmanager/nicmanager_test.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package nicmanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-acme/lego/v4/platform/tester"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const envDomain = envNamespace + "DOMAIN"
|
||||||
|
|
||||||
|
var envTest = tester.NewEnvTest(EnvUsername, EnvLogin, EnvEmail, EnvPassword, EnvOTP).
|
||||||
|
WithDomain(envDomain)
|
||||||
|
|
||||||
|
func TestNewDNSProvider(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
envVars map[string]string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "success (email)",
|
||||||
|
envVars: map[string]string{
|
||||||
|
EnvEmail: "foo@example.com",
|
||||||
|
EnvPassword: "secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "success (login.username)",
|
||||||
|
envVars: map[string]string{
|
||||||
|
EnvLogin: "foo",
|
||||||
|
EnvUsername: "bar",
|
||||||
|
EnvPassword: "secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing credentials",
|
||||||
|
expected: "nicmanager: some credentials information are missing: NICMANAGER_API_PASSWORD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing password",
|
||||||
|
envVars: map[string]string{
|
||||||
|
EnvEmail: "foo@example.com",
|
||||||
|
},
|
||||||
|
expected: "nicmanager: some credentials information are missing: NICMANAGER_API_PASSWORD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing username",
|
||||||
|
envVars: map[string]string{
|
||||||
|
EnvLogin: "foo",
|
||||||
|
EnvPassword: "secret",
|
||||||
|
},
|
||||||
|
expected: "nicmanager: credentials missing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing login",
|
||||||
|
envVars: map[string]string{
|
||||||
|
EnvUsername: "bar",
|
||||||
|
EnvPassword: "secret",
|
||||||
|
},
|
||||||
|
expected: "nicmanager: credentials missing",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
defer envTest.RestoreEnv()
|
||||||
|
envTest.ClearEnv()
|
||||||
|
|
||||||
|
envTest.Apply(test.envVars)
|
||||||
|
|
||||||
|
p, err := NewDNSProvider()
|
||||||
|
|
||||||
|
if test.expected == "" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, p)
|
||||||
|
require.NotNil(t, p.config)
|
||||||
|
require.NotNil(t, p.client)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, err, test.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewDNSProviderConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
login string
|
||||||
|
username string
|
||||||
|
email string
|
||||||
|
password string
|
||||||
|
otpSecret string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "success (email)",
|
||||||
|
email: "foo@example.com",
|
||||||
|
password: "secret",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "success (login.username)",
|
||||||
|
login: "john",
|
||||||
|
username: "doe",
|
||||||
|
password: "secret",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing credentials",
|
||||||
|
expected: "nicmanager: credentials missing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing password",
|
||||||
|
email: "foo@example.com",
|
||||||
|
expected: "nicmanager: credentials missing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing login",
|
||||||
|
login: "",
|
||||||
|
username: "doe",
|
||||||
|
password: "secret",
|
||||||
|
expected: "nicmanager: credentials missing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing username",
|
||||||
|
login: "john",
|
||||||
|
username: "",
|
||||||
|
password: "secret",
|
||||||
|
expected: "nicmanager: credentials missing",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
config := NewDefaultConfig()
|
||||||
|
config.Login = test.login
|
||||||
|
config.Username = test.username
|
||||||
|
config.Email = test.email
|
||||||
|
config.Password = test.password
|
||||||
|
config.OTPSecret = test.otpSecret
|
||||||
|
|
||||||
|
p, err := NewDNSProviderConfig(config)
|
||||||
|
|
||||||
|
if test.expected == "" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, p)
|
||||||
|
require.NotNil(t, p.config)
|
||||||
|
require.NotNil(t, p.client)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, err, test.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLivePresent(t *testing.T) {
|
||||||
|
if !envTest.IsLiveTest() {
|
||||||
|
t.Skip("skipping live test")
|
||||||
|
}
|
||||||
|
|
||||||
|
envTest.RestoreEnv()
|
||||||
|
provider, err := NewDNSProvider()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = provider.Present(envTest.GetDomain(), "", "123d==")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLiveCleanUp(t *testing.T) {
|
||||||
|
if !envTest.IsLiveTest() {
|
||||||
|
t.Skip("skipping live test")
|
||||||
|
}
|
||||||
|
|
||||||
|
envTest.RestoreEnv()
|
||||||
|
provider, err := NewDNSProvider()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user