forked from TrueCloudLab/lego
feat: joker username and password. (#927)
This commit is contained in:
parent
84d9795793
commit
0dbc28193b
7 changed files with 214 additions and 38 deletions
|
@ -762,6 +762,8 @@ func displayDNSHelp(name string) error {
|
|||
|
||||
ew.writeln(`Credentials:`)
|
||||
ew.writeln(` - "JOKER_API_KEY": API key`)
|
||||
ew.writeln(` - "JOKER_PASSWORD": Joker.com password`)
|
||||
ew.writeln(` - "JOKER_USERNAME": Joker.com username (email address)`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Additional Configuration:`)
|
||||
|
|
|
@ -21,6 +21,10 @@ Configuration for [Joker](https://joker.com).
|
|||
Here is an example bash command using the Joker provider:
|
||||
|
||||
```bash
|
||||
JOKER_USERNAME=<your email> \
|
||||
JOKER_PASSWORD=<your password> \
|
||||
lego --dns joker --domains my.domain.com --email my@email.com run
|
||||
# or
|
||||
JOKER_API_KEY=<your API key> \
|
||||
lego --dns joker --domains my.domain.com --email my@email.com run
|
||||
```
|
||||
|
@ -33,6 +37,8 @@ lego --dns joker --domains my.domain.com --email my@email.com run
|
|||
| Environment Variable Name | Description |
|
||||
|-----------------------|-------------|
|
||||
| `JOKER_API_KEY` | API key |
|
||||
| `JOKER_PASSWORD` | Joker.com password |
|
||||
| `JOKER_USERNAME` | Joker.com username (email address) |
|
||||
|
||||
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).
|
||||
|
|
|
@ -70,7 +70,20 @@ func (d *DNSProvider) login() (*response, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
response, err := d.postRequest("login", url.Values{"api-key": {d.config.APIKey}})
|
||||
var values url.Values
|
||||
switch {
|
||||
case d.config.Username != "" && d.config.Password != "":
|
||||
values = url.Values{
|
||||
"username": {d.config.Username},
|
||||
"password": {d.config.Password},
|
||||
}
|
||||
case d.config.APIKey != "":
|
||||
values = url.Values{"api-key": {d.config.APIKey}}
|
||||
default:
|
||||
return nil, fmt.Errorf("no username and password or api-key")
|
||||
}
|
||||
|
||||
response, err := d.postRequest("login", values)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
|
|
@ -12,9 +12,15 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
correctAuth = "123"
|
||||
incorrectAuth = "321"
|
||||
serverErrorAuth = "500"
|
||||
correctAPIKey = "123"
|
||||
incorrectAPIKey = "321"
|
||||
serverErrorAPIKey = "500"
|
||||
)
|
||||
|
||||
const (
|
||||
correctUsername = "lego"
|
||||
incorrectUsername = "not_lego"
|
||||
serverErrorUsername = "error"
|
||||
)
|
||||
|
||||
func setup() (*http.ServeMux, *httptest.Server) {
|
||||
|
@ -23,35 +29,35 @@ func setup() (*http.ServeMux, *httptest.Server) {
|
|||
return mux, server
|
||||
}
|
||||
|
||||
func TestDNSProvider_login(t *testing.T) {
|
||||
func TestDNSProvider_login_api_key(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
authKey string
|
||||
apiKey string
|
||||
expectedError bool
|
||||
expectedStatusCode int
|
||||
expectedAuthSid string
|
||||
}{
|
||||
{
|
||||
desc: "correct key",
|
||||
authKey: correctAuth,
|
||||
apiKey: correctAPIKey,
|
||||
expectedStatusCode: 0,
|
||||
expectedAuthSid: correctAuth,
|
||||
expectedAuthSid: correctAPIKey,
|
||||
},
|
||||
{
|
||||
desc: "incorrect key",
|
||||
authKey: incorrectAuth,
|
||||
apiKey: incorrectAPIKey,
|
||||
expectedStatusCode: 2200,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "server error",
|
||||
authKey: serverErrorAuth,
|
||||
apiKey: serverErrorAPIKey,
|
||||
expectedStatusCode: -500,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "non-ok status code",
|
||||
authKey: "333",
|
||||
apiKey: "333",
|
||||
expectedStatusCode: 2202,
|
||||
expectedError: true,
|
||||
},
|
||||
|
@ -64,11 +70,11 @@ func TestDNSProvider_login(t *testing.T) {
|
|||
require.Equal(t, "POST", r.Method)
|
||||
|
||||
switch r.FormValue("api-key") {
|
||||
case correctAuth:
|
||||
case correctAPIKey:
|
||||
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\nAuth-Sid: 123\n\ncom\nnet")
|
||||
case incorrectAuth:
|
||||
case incorrectAPIKey:
|
||||
_, _ = io.WriteString(w, "Status-Code: 2200\nStatus-Text: Authentication error")
|
||||
case serverErrorAuth:
|
||||
case serverErrorAPIKey:
|
||||
http.NotFound(w, r)
|
||||
default:
|
||||
_, _ = io.WriteString(w, "Status-Code: 2202\nStatus-Text: OK\n\ncom\nnet")
|
||||
|
@ -79,7 +85,89 @@ func TestDNSProvider_login(t *testing.T) {
|
|||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.BaseURL = server.URL
|
||||
config.APIKey = test.authKey
|
||||
config.APIKey = test.apiKey
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
|
||||
response, err := p.login()
|
||||
if test.expectedError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, response)
|
||||
assert.Equal(t, test.expectedStatusCode, response.StatusCode)
|
||||
assert.Equal(t, test.expectedAuthSid, response.AuthSid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSProvider_login_username(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
username string
|
||||
password string
|
||||
expectedError bool
|
||||
expectedStatusCode int
|
||||
expectedAuthSid string
|
||||
}{
|
||||
{
|
||||
desc: "correct username and password",
|
||||
username: correctUsername,
|
||||
password: "go-acme",
|
||||
expectedError: false,
|
||||
expectedStatusCode: 0,
|
||||
expectedAuthSid: correctAPIKey,
|
||||
},
|
||||
{
|
||||
desc: "incorrect username",
|
||||
username: incorrectUsername,
|
||||
password: "go-acme",
|
||||
expectedStatusCode: 2200,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "server error",
|
||||
username: serverErrorUsername,
|
||||
password: "go-acme",
|
||||
expectedStatusCode: -500,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "non-ok status code",
|
||||
username: "random",
|
||||
password: "go-acme",
|
||||
expectedStatusCode: 2202,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
mux, server := setup()
|
||||
defer server.Close()
|
||||
|
||||
mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "POST", r.Method)
|
||||
|
||||
switch r.FormValue("username") {
|
||||
case correctUsername:
|
||||
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\nAuth-Sid: 123\n\ncom\nnet")
|
||||
case incorrectUsername:
|
||||
_, _ = io.WriteString(w, "Status-Code: 2200\nStatus-Text: Authentication error")
|
||||
case serverErrorUsername:
|
||||
http.NotFound(w, r)
|
||||
default:
|
||||
_, _ = io.WriteString(w, "Status-Code: 2202\nStatus-Text: OK\n\ncom\nnet")
|
||||
}
|
||||
})
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.BaseURL = server.URL
|
||||
config.Username = test.username
|
||||
config.Password = test.password
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
@ -107,12 +195,12 @@ func TestDNSProvider_logout(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "correct auth-sid",
|
||||
authSid: correctAuth,
|
||||
authSid: correctAPIKey,
|
||||
expectedStatusCode: 0,
|
||||
},
|
||||
{
|
||||
desc: "incorrect auth-sid",
|
||||
authSid: incorrectAuth,
|
||||
authSid: incorrectAPIKey,
|
||||
expectedStatusCode: 2200,
|
||||
},
|
||||
{
|
||||
|
@ -122,7 +210,7 @@ func TestDNSProvider_logout(t *testing.T) {
|
|||
},
|
||||
{
|
||||
desc: "server error",
|
||||
authSid: serverErrorAuth,
|
||||
authSid: serverErrorAPIKey,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
@ -134,9 +222,9 @@ func TestDNSProvider_logout(t *testing.T) {
|
|||
require.Equal(t, "POST", r.Method)
|
||||
|
||||
switch r.FormValue("auth-sid") {
|
||||
case correctAuth:
|
||||
case correctAPIKey:
|
||||
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\n")
|
||||
case incorrectAuth:
|
||||
case incorrectAPIKey:
|
||||
_, _ = io.WriteString(w, "Status-Code: 2200\nStatus-Text: Authentication error")
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
|
@ -179,20 +267,20 @@ func TestDNSProvider_getZone(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
desc: "correct auth-sid, known domain",
|
||||
authSid: correctAuth,
|
||||
authSid: correctAPIKey,
|
||||
domain: "known",
|
||||
zone: testZone,
|
||||
expectedStatusCode: 0,
|
||||
},
|
||||
{
|
||||
desc: "incorrect auth-sid, known domain",
|
||||
authSid: incorrectAuth,
|
||||
authSid: incorrectAPIKey,
|
||||
domain: "known",
|
||||
expectedStatusCode: 2202,
|
||||
},
|
||||
{
|
||||
desc: "correct auth-sid, unknown domain",
|
||||
authSid: correctAuth,
|
||||
authSid: correctAPIKey,
|
||||
domain: "unknown",
|
||||
expectedStatusCode: 2202,
|
||||
},
|
||||
|
@ -213,9 +301,9 @@ func TestDNSProvider_getZone(t *testing.T) {
|
|||
domain := r.FormValue("domain")
|
||||
|
||||
switch {
|
||||
case authSid == correctAuth && domain == "known":
|
||||
case authSid == correctAPIKey && domain == "known":
|
||||
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\n\n"+testZone)
|
||||
case authSid == incorrectAuth || (authSid == correctAuth && domain == "unknown"):
|
||||
case authSid == incorrectAPIKey || (authSid == correctAPIKey && domain == "unknown"):
|
||||
_, _ = io.WriteString(w, "Status-Code: 2202\nStatus-Text: Authorization error")
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
|
|
|
@ -18,6 +18,8 @@ type Config struct {
|
|||
Debug bool
|
||||
BaseURL string
|
||||
APIKey string
|
||||
Username string
|
||||
Password string
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
|
@ -50,11 +52,17 @@ type DNSProvider struct {
|
|||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("JOKER_API_KEY")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("joker: %v", err)
|
||||
var errU error
|
||||
values, errU = env.Get("JOKER_USERNAME", "JOKER_PASSWORD")
|
||||
if errU != nil {
|
||||
return nil, fmt.Errorf("joker: %v or %v", errU, err)
|
||||
}
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = values["JOKER_API_KEY"]
|
||||
config.Username = values["JOKER_USERNAME"]
|
||||
config.Password = values["JOKER_PASSWORD"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
@ -66,8 +74,10 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||
}
|
||||
|
||||
if config.APIKey == "" {
|
||||
if config.Username == "" || config.Password == "" {
|
||||
return nil, fmt.Errorf("joker: credentials missing")
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(config.BaseURL, "/") {
|
||||
config.BaseURL += "/"
|
||||
|
|
|
@ -5,6 +5,10 @@ Code = "joker"
|
|||
Since = "v2.6.0"
|
||||
|
||||
Example = '''
|
||||
JOKER_USERNAME=<your email> \
|
||||
JOKER_PASSWORD=<your password> \
|
||||
lego --dns joker --domains my.domain.com --email my@email.com run
|
||||
# or
|
||||
JOKER_API_KEY=<your API key> \
|
||||
lego --dns joker --domains my.domain.com --email my@email.com run
|
||||
'''
|
||||
|
@ -12,6 +16,8 @@ lego --dns joker --domains my.domain.com --email my@email.com run
|
|||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
JOKER_API_KEY = "API key"
|
||||
JOKER_USERNAME = "Joker.com username (email address)"
|
||||
JOKER_PASSWORD = "Joker.com password"
|
||||
[Configuration.Additional]
|
||||
JOKER_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||
JOKER_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||
|
|
|
@ -9,7 +9,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var envTest = tester.NewEnvTest("JOKER_API_KEY").WithDomain("JOKER_DOMAIN")
|
||||
var envTest = tester.NewEnvTest("JOKER_API_KEY", "JOKER_USERNAME", "JOKER_PASSWORD").
|
||||
WithDomain("JOKER_DOMAIN")
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
|
@ -18,17 +19,44 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
desc: "success API key",
|
||||
envVars: map[string]string{
|
||||
"JOKER_API_KEY": "123",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing key",
|
||||
desc: "success username password",
|
||||
envVars: map[string]string{
|
||||
"JOKER_USERNAME": "123",
|
||||
"JOKER_PASSWORD": "123",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing credentials",
|
||||
envVars: map[string]string{
|
||||
"JOKER_API_KEY": "",
|
||||
"JOKER_USERNAME": "",
|
||||
"JOKER_PASSWORD": "",
|
||||
},
|
||||
expected: "joker: some credentials information are missing: JOKER_API_KEY",
|
||||
expected: "joker: some credentials information are missing: JOKER_USERNAME,JOKER_PASSWORD or some credentials information are missing: JOKER_API_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing password",
|
||||
envVars: map[string]string{
|
||||
"JOKER_API_KEY": "",
|
||||
"JOKER_USERNAME": "123",
|
||||
"JOKER_PASSWORD": "",
|
||||
},
|
||||
expected: "joker: some credentials information are missing: JOKER_PASSWORD or some credentials information are missing: JOKER_API_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing username",
|
||||
envVars: map[string]string{
|
||||
"JOKER_API_KEY": "",
|
||||
"JOKER_USERNAME": "",
|
||||
"JOKER_PASSWORD": "123",
|
||||
},
|
||||
expected: "joker: some credentials information are missing: JOKER_USERNAME or some credentials information are missing: JOKER_API_KEY",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -55,14 +83,22 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
authKey string
|
||||
apiKey string
|
||||
username string
|
||||
password string
|
||||
baseURL string
|
||||
expected string
|
||||
expectedBaseURL string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
authKey: "123",
|
||||
desc: "success api key",
|
||||
apiKey: "123",
|
||||
expectedBaseURL: defaultBaseURL,
|
||||
},
|
||||
{
|
||||
desc: "success username and password",
|
||||
username: "123",
|
||||
password: "123",
|
||||
expectedBaseURL: defaultBaseURL,
|
||||
},
|
||||
{
|
||||
|
@ -70,15 +106,27 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
expected: "joker: credentials missing",
|
||||
expectedBaseURL: defaultBaseURL,
|
||||
},
|
||||
{
|
||||
desc: "missing credentials: username",
|
||||
expected: "joker: credentials missing",
|
||||
username: "123",
|
||||
expectedBaseURL: defaultBaseURL,
|
||||
},
|
||||
{
|
||||
desc: "missing credentials: password",
|
||||
expected: "joker: credentials missing",
|
||||
password: "123",
|
||||
expectedBaseURL: defaultBaseURL,
|
||||
},
|
||||
{
|
||||
desc: "Base URL should ends with /",
|
||||
authKey: "123",
|
||||
apiKey: "123",
|
||||
baseURL: "http://example.com",
|
||||
expectedBaseURL: "http://example.com/",
|
||||
},
|
||||
{
|
||||
desc: "Base URL already ends with /",
|
||||
authKey: "123",
|
||||
apiKey: "123",
|
||||
baseURL: "http://example.com/",
|
||||
expectedBaseURL: "http://example.com/",
|
||||
},
|
||||
|
@ -87,7 +135,10 @@ func TestNewDNSProviderConfig(t *testing.T) {
|
|||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.APIKey = test.authKey
|
||||
config.APIKey = test.apiKey
|
||||
config.Username = test.username
|
||||
config.Password = test.password
|
||||
|
||||
if test.baseURL != "" {
|
||||
config.BaseURL = test.baseURL
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue