Skip to content

Commit

Permalink
inwx: wait before generating new TOTP TANs (#2084)
Browse files Browse the repository at this point in the history
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
  • Loading branch information
gnoack and ldez committed Jan 18, 2024
1 parent c17f659 commit 9d4c60e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 2 deletions.
28 changes: 26 additions & 2 deletions providers/dns/inwx/inwx.go
Expand Up @@ -51,8 +51,9 @@ func NewDefaultConfig() *Config {

// DNSProvider implements the challenge.Provider interface.
type DNSProvider struct {
config *Config
client *goinwx.Client
config *Config
client *goinwx.Client
previousUnlock time.Time
}

// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
Expand Down Expand Up @@ -202,10 +203,33 @@ func (d *DNSProvider) twoFactorAuth(info *goinwx.LoginResponse) error {
return errors.New("two-factor authentication but no shared secret is given")
}

// INWX forbids re-authentication with a previously used TAN.
// To avoid using the same TAN twice, we wait until the next TOTP period.
sleep := d.computeSleep(time.Now())
if sleep != 0 {
log.Infof("inwx: waiting %s for next TOTP token", sleep)
time.Sleep(sleep)
}

tan, err := totp.GenerateCode(d.config.SharedSecret, time.Now())
if err != nil {
return err
}

d.previousUnlock = time.Now()

return d.client.Account.Unlock(tan)
}

func (d *DNSProvider) computeSleep(now time.Time) time.Duration {
if d.previousUnlock.IsZero() {
return 0 * time.Second
}

endPeriod := d.previousUnlock.Add(30 * time.Second)
if endPeriod.After(now) {
return endPeriod.Sub(now)
}

return 0 * time.Second
}
44 changes: 44 additions & 0 deletions providers/dns/inwx/inwx_test.go
Expand Up @@ -2,8 +2,10 @@ package inwx

import (
"testing"
"time"

"github.com/go-acme/lego/v4/platform/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -141,3 +143,45 @@ func TestLivePresentAndCleanup(t *testing.T) {
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
require.NoError(t, err)
}

func Test_computeSleep(t *testing.T) {
testCases := []struct {
desc string
previous string
expected time.Duration
}{
{
desc: "after 30s",
previous: "2024-01-01T06:29:20Z",
expected: 0 * time.Second,
},
{
desc: "0s",
previous: "2024-01-01T06:29:30Z",
expected: 0 * time.Second,
},
{
desc: "before 30s",
previous: "2024-01-01T06:29:50Z", // 10 s
expected: 20 * time.Second,
},
}

now, err := time.Parse(time.RFC3339, "2024-01-01T06:30:00Z")
require.NoError(t, err)

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

previous, err := time.Parse(time.RFC3339, test.previous)
require.NoError(t, err)

d := &DNSProvider{previousUnlock: previous}

sleep := d.computeSleep(now)
assert.Equal(t, test.expected, sleep)
})
}
}

0 comments on commit 9d4c60e

Please sign in to comment.