feat: joker username and password. (#927)

This commit is contained in:
Ludovic Fernandez 2019-07-17 02:39:17 +02:00 committed by GitHub
parent 84d9795793
commit 0dbc28193b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 214 additions and 38 deletions

View file

@ -762,6 +762,8 @@ func displayDNSHelp(name string) error {
ew.writeln(`Credentials:`) ew.writeln(`Credentials:`)
ew.writeln(` - "JOKER_API_KEY": API key`) 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()
ew.writeln(`Additional Configuration:`) ew.writeln(`Additional Configuration:`)

View file

@ -21,6 +21,10 @@ Configuration for [Joker](https://joker.com).
Here is an example bash command using the Joker provider: Here is an example bash command using the Joker provider:
```bash ```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> \ JOKER_API_KEY=<your API key> \
lego --dns joker --domains my.domain.com --email my@email.com run 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 | | Environment Variable Name | Description |
|-----------------------|-------------| |-----------------------|-------------|
| `JOKER_API_KEY` | API key | | `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. 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). More information [here](/lego/dns/#configuration-and-credentials).

View file

@ -70,7 +70,20 @@ func (d *DNSProvider) login() (*response, error) {
return nil, nil 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 { if err != nil {
return response, err return response, err
} }

View file

@ -12,9 +12,15 @@ import (
) )
const ( const (
correctAuth = "123" correctAPIKey = "123"
incorrectAuth = "321" incorrectAPIKey = "321"
serverErrorAuth = "500" serverErrorAPIKey = "500"
)
const (
correctUsername = "lego"
incorrectUsername = "not_lego"
serverErrorUsername = "error"
) )
func setup() (*http.ServeMux, *httptest.Server) { func setup() (*http.ServeMux, *httptest.Server) {
@ -23,35 +29,35 @@ func setup() (*http.ServeMux, *httptest.Server) {
return mux, server return mux, server
} }
func TestDNSProvider_login(t *testing.T) { func TestDNSProvider_login_api_key(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
authKey string apiKey string
expectedError bool expectedError bool
expectedStatusCode int expectedStatusCode int
expectedAuthSid string expectedAuthSid string
}{ }{
{ {
desc: "correct key", desc: "correct key",
authKey: correctAuth, apiKey: correctAPIKey,
expectedStatusCode: 0, expectedStatusCode: 0,
expectedAuthSid: correctAuth, expectedAuthSid: correctAPIKey,
}, },
{ {
desc: "incorrect key", desc: "incorrect key",
authKey: incorrectAuth, apiKey: incorrectAPIKey,
expectedStatusCode: 2200, expectedStatusCode: 2200,
expectedError: true, expectedError: true,
}, },
{ {
desc: "server error", desc: "server error",
authKey: serverErrorAuth, apiKey: serverErrorAPIKey,
expectedStatusCode: -500, expectedStatusCode: -500,
expectedError: true, expectedError: true,
}, },
{ {
desc: "non-ok status code", desc: "non-ok status code",
authKey: "333", apiKey: "333",
expectedStatusCode: 2202, expectedStatusCode: 2202,
expectedError: true, expectedError: true,
}, },
@ -64,11 +70,11 @@ func TestDNSProvider_login(t *testing.T) {
require.Equal(t, "POST", r.Method) require.Equal(t, "POST", r.Method)
switch r.FormValue("api-key") { switch r.FormValue("api-key") {
case correctAuth: case correctAPIKey:
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\nAuth-Sid: 123\n\ncom\nnet") _, _ = 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") _, _ = io.WriteString(w, "Status-Code: 2200\nStatus-Text: Authentication error")
case serverErrorAuth: case serverErrorAPIKey:
http.NotFound(w, r) http.NotFound(w, r)
default: default:
_, _ = io.WriteString(w, "Status-Code: 2202\nStatus-Text: OK\n\ncom\nnet") _, _ = 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) { t.Run(test.desc, func(t *testing.T) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.BaseURL = server.URL 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) p, err := NewDNSProviderConfig(config)
require.NoError(t, err) require.NoError(t, err)
@ -107,12 +195,12 @@ func TestDNSProvider_logout(t *testing.T) {
}{ }{
{ {
desc: "correct auth-sid", desc: "correct auth-sid",
authSid: correctAuth, authSid: correctAPIKey,
expectedStatusCode: 0, expectedStatusCode: 0,
}, },
{ {
desc: "incorrect auth-sid", desc: "incorrect auth-sid",
authSid: incorrectAuth, authSid: incorrectAPIKey,
expectedStatusCode: 2200, expectedStatusCode: 2200,
}, },
{ {
@ -122,7 +210,7 @@ func TestDNSProvider_logout(t *testing.T) {
}, },
{ {
desc: "server error", desc: "server error",
authSid: serverErrorAuth, authSid: serverErrorAPIKey,
expectedError: true, expectedError: true,
}, },
} }
@ -134,9 +222,9 @@ func TestDNSProvider_logout(t *testing.T) {
require.Equal(t, "POST", r.Method) require.Equal(t, "POST", r.Method)
switch r.FormValue("auth-sid") { switch r.FormValue("auth-sid") {
case correctAuth: case correctAPIKey:
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\n") _, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\n")
case incorrectAuth: case incorrectAPIKey:
_, _ = io.WriteString(w, "Status-Code: 2200\nStatus-Text: Authentication error") _, _ = io.WriteString(w, "Status-Code: 2200\nStatus-Text: Authentication error")
default: default:
http.NotFound(w, r) http.NotFound(w, r)
@ -179,20 +267,20 @@ func TestDNSProvider_getZone(t *testing.T) {
}{ }{
{ {
desc: "correct auth-sid, known domain", desc: "correct auth-sid, known domain",
authSid: correctAuth, authSid: correctAPIKey,
domain: "known", domain: "known",
zone: testZone, zone: testZone,
expectedStatusCode: 0, expectedStatusCode: 0,
}, },
{ {
desc: "incorrect auth-sid, known domain", desc: "incorrect auth-sid, known domain",
authSid: incorrectAuth, authSid: incorrectAPIKey,
domain: "known", domain: "known",
expectedStatusCode: 2202, expectedStatusCode: 2202,
}, },
{ {
desc: "correct auth-sid, unknown domain", desc: "correct auth-sid, unknown domain",
authSid: correctAuth, authSid: correctAPIKey,
domain: "unknown", domain: "unknown",
expectedStatusCode: 2202, expectedStatusCode: 2202,
}, },
@ -213,9 +301,9 @@ func TestDNSProvider_getZone(t *testing.T) {
domain := r.FormValue("domain") domain := r.FormValue("domain")
switch { switch {
case authSid == correctAuth && domain == "known": case authSid == correctAPIKey && domain == "known":
_, _ = io.WriteString(w, "Status-Code: 0\nStatus-Text: OK\n\n"+testZone) _, _ = 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") _, _ = io.WriteString(w, "Status-Code: 2202\nStatus-Text: Authorization error")
default: default:
http.NotFound(w, r) http.NotFound(w, r)

View file

@ -18,6 +18,8 @@ type Config struct {
Debug bool Debug bool
BaseURL string BaseURL string
APIKey string APIKey string
Username string
Password string
PropagationTimeout time.Duration PropagationTimeout time.Duration
PollingInterval time.Duration PollingInterval time.Duration
TTL int TTL int
@ -50,11 +52,17 @@ type DNSProvider struct {
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get("JOKER_API_KEY") values, err := env.Get("JOKER_API_KEY")
if err != nil { 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 := NewDefaultConfig()
config.APIKey = values["JOKER_API_KEY"] config.APIKey = values["JOKER_API_KEY"]
config.Username = values["JOKER_USERNAME"]
config.Password = values["JOKER_PASSWORD"]
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }
@ -66,7 +74,9 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
} }
if config.APIKey == "" { if config.APIKey == "" {
return nil, fmt.Errorf("joker: credentials missing") if config.Username == "" || config.Password == "" {
return nil, fmt.Errorf("joker: credentials missing")
}
} }
if !strings.HasSuffix(config.BaseURL, "/") { if !strings.HasSuffix(config.BaseURL, "/") {

View file

@ -5,6 +5,10 @@ Code = "joker"
Since = "v2.6.0" Since = "v2.6.0"
Example = ''' 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> \ JOKER_API_KEY=<your API key> \
lego --dns joker --domains my.domain.com --email my@email.com run 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]
[Configuration.Credentials] [Configuration.Credentials]
JOKER_API_KEY = "API key" JOKER_API_KEY = "API key"
JOKER_USERNAME = "Joker.com username (email address)"
JOKER_PASSWORD = "Joker.com password"
[Configuration.Additional] [Configuration.Additional]
JOKER_POLLING_INTERVAL = "Time between DNS propagation check" JOKER_POLLING_INTERVAL = "Time between DNS propagation check"
JOKER_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" JOKER_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"

View file

@ -9,7 +9,8 @@ import (
"github.com/stretchr/testify/require" "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) { func TestNewDNSProvider(t *testing.T) {
testCases := []struct { testCases := []struct {
@ -18,17 +19,44 @@ func TestNewDNSProvider(t *testing.T) {
expected string expected string
}{ }{
{ {
desc: "success", desc: "success API key",
envVars: map[string]string{ envVars: map[string]string{
"JOKER_API_KEY": "123", "JOKER_API_KEY": "123",
}, },
}, },
{ {
desc: "missing key", desc: "success username password",
envVars: map[string]string{ envVars: map[string]string{
"JOKER_API_KEY": "", "JOKER_USERNAME": "123",
"JOKER_PASSWORD": "123",
}, },
expected: "joker: some credentials information are missing: JOKER_API_KEY", },
{
desc: "missing credentials",
envVars: map[string]string{
"JOKER_API_KEY": "",
"JOKER_USERNAME": "",
"JOKER_PASSWORD": "",
},
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) { func TestNewDNSProviderConfig(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
authKey string apiKey string
username string
password string
baseURL string baseURL string
expected string expected string
expectedBaseURL string expectedBaseURL string
}{ }{
{ {
desc: "success", desc: "success api key",
authKey: "123", apiKey: "123",
expectedBaseURL: defaultBaseURL,
},
{
desc: "success username and password",
username: "123",
password: "123",
expectedBaseURL: defaultBaseURL, expectedBaseURL: defaultBaseURL,
}, },
{ {
@ -70,15 +106,27 @@ func TestNewDNSProviderConfig(t *testing.T) {
expected: "joker: credentials missing", expected: "joker: credentials missing",
expectedBaseURL: defaultBaseURL, 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 /", desc: "Base URL should ends with /",
authKey: "123", apiKey: "123",
baseURL: "http://example.com", baseURL: "http://example.com",
expectedBaseURL: "http://example.com/", expectedBaseURL: "http://example.com/",
}, },
{ {
desc: "Base URL already ends with /", desc: "Base URL already ends with /",
authKey: "123", apiKey: "123",
baseURL: "http://example.com/", baseURL: "http://example.com/",
expectedBaseURL: "http://example.com/", expectedBaseURL: "http://example.com/",
}, },
@ -87,7 +135,10 @@ func TestNewDNSProviderConfig(t *testing.T) {
for _, test := range testCases { for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.APIKey = test.authKey config.APIKey = test.apiKey
config.Username = test.username
config.Password = test.password
if test.baseURL != "" { if test.baseURL != "" {
config.BaseURL = test.baseURL config.BaseURL = test.baseURL
} }