boulder/ratelimits/names_test.go

498 lines
14 KiB
Go

package ratelimits
import (
"fmt"
"net/netip"
"strings"
"testing"
"github.com/letsencrypt/boulder/identifier"
"github.com/letsencrypt/boulder/test"
)
func TestNameIsValid(t *testing.T) {
t.Parallel()
type args struct {
name Name
}
tests := []struct {
name string
args args
want bool
}{
{name: "Unknown", args: args{name: Unknown}, want: false},
{name: "9001", args: args{name: 9001}, want: false},
{name: "NewRegistrationsPerIPAddress", args: args{name: NewRegistrationsPerIPAddress}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.args.name.isValid()
test.AssertEquals(t, tt.want, got)
})
}
}
func TestValidateIdForName(t *testing.T) {
t.Parallel()
testCases := []struct {
limit Name
desc string
id string
err string
}{
{
limit: NewRegistrationsPerIPAddress,
desc: "valid IPv4 address",
id: "64.112.117.1",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "reserved IPv4 address",
id: "10.0.0.1",
err: "in a reserved address block",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "valid IPv6 address",
id: "2602:80a:6000::42:42",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "IPv6 address in non-canonical form",
id: "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
err: "must be in canonical form",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "empty string",
id: "",
err: "must be an IP address",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "one space",
id: " ",
err: "must be an IP address",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "invalid IPv4 address",
id: "10.0.0.9000",
err: "must be an IP address",
},
{
limit: NewRegistrationsPerIPAddress,
desc: "invalid IPv6 address",
id: "2001:0db8:85a3:0000:0000:8a2e:0370:7334:9000",
err: "must be an IP address",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "valid IPv6 address range",
id: "2602:80a:6000::/48",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "IPv6 address range in non-canonical form",
id: "2602:080a:6000::/48",
err: "must be in canonical form",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "IPv6 address range with low bits set",
id: "2602:080a:6000::1/48",
err: "must be in canonical form",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "invalid IPv6 CIDR range",
id: "2001:0db8:0000::/128",
err: "must be /48",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "invalid IPv6 CIDR",
id: "2001:0db8:0000::/48/48",
err: "must be an IPv6 CIDR range",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "IPv4 CIDR when we expect IPv6 CIDR range",
id: "10.0.0.0/16",
err: "must be /48",
},
{
limit: NewRegistrationsPerIPv6Range,
desc: "IPv4 CIDR with invalid long mask",
id: "10.0.0.0/48",
err: "must be an IPv6 CIDR range",
},
{
limit: NewOrdersPerAccount,
desc: "valid regId",
id: "1234567890",
},
{
limit: NewOrdersPerAccount,
desc: "invalid regId",
id: "lol",
err: "must be an ACME registration Id",
},
{
limit: FailedAuthorizationsPerDomainPerAccount,
desc: "transaction: valid regId and domain",
id: "12345:example.com",
},
{
limit: FailedAuthorizationsPerDomainPerAccount,
desc: "transaction: invalid regId",
id: "12ea5:example.com",
err: "invalid regId",
},
{
limit: FailedAuthorizationsPerDomainPerAccount,
desc: "transaction: invalid domain",
id: "12345:examplecom",
err: "name needs at least one dot",
},
{
limit: FailedAuthorizationsPerDomainPerAccount,
desc: "override: valid regId",
id: "12345",
},
{
limit: FailedAuthorizationsPerDomainPerAccount,
desc: "override: invalid regId",
id: "12ea5",
err: "invalid regId",
},
{
limit: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "transaction: valid regId and domain",
id: "12345:example.com",
},
{
limit: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "transaction: invalid regId",
id: "12ea5:example.com",
err: "invalid regId",
},
{
limit: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "transaction: invalid domain",
id: "12345:examplecom",
err: "name needs at least one dot",
},
{
limit: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "override: valid regId",
id: "12345",
},
{
limit: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "override: invalid regId",
id: "12ea5",
err: "invalid regId",
},
{
limit: CertificatesPerDomainPerAccount,
desc: "transaction: valid regId and domain",
id: "12345:example.com",
},
{
limit: CertificatesPerDomainPerAccount,
desc: "transaction: invalid regId",
id: "12ea5:example.com",
err: "invalid regId",
},
{
limit: CertificatesPerDomainPerAccount,
desc: "transaction: invalid domain",
id: "12345:examplecom",
err: "name needs at least one dot",
},
{
limit: CertificatesPerDomainPerAccount,
desc: "override: valid regId",
id: "12345",
},
{
limit: CertificatesPerDomainPerAccount,
desc: "override: invalid regId",
id: "12ea5",
err: "invalid regId",
},
{
limit: CertificatesPerDomain,
desc: "valid domain",
id: "example.com",
},
{
limit: CertificatesPerDomain,
desc: "valid IPv4 address",
id: "64.112.117.1",
},
{
limit: CertificatesPerDomain,
desc: "valid IPv6 address",
id: "2602:80a:6000::",
},
{
limit: CertificatesPerDomain,
desc: "IPv6 address with subnet",
id: "2602:80a:6000::/64",
err: "nor an IP address",
},
{
limit: CertificatesPerDomain,
desc: "malformed domain",
id: "example:.com",
err: "name contains an invalid character",
},
{
limit: CertificatesPerDomain,
desc: "empty domain",
id: "",
err: "Identifier value (name) is empty",
},
{
limit: CertificatesPerFQDNSet,
desc: "valid fqdnSet containing a single domain",
id: "example.com",
},
{
limit: CertificatesPerFQDNSet,
desc: "valid fqdnSet containing a single IPv4 address",
id: "64.112.117.1",
},
{
limit: CertificatesPerFQDNSet,
desc: "valid fqdnSet containing a single IPv6 address",
id: "2602:80a:6000::1",
},
{
limit: CertificatesPerFQDNSet,
desc: "valid fqdnSet containing multiple domains",
id: "example.com,example.org",
},
{
limit: CertificatesPerFQDNSet,
desc: "valid fqdnSet containing multiple domains and IPs",
id: "2602:80a:6000::1,64.112.117.1,example.com,example.org",
},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%s/%s", tc.limit, tc.desc), func(t *testing.T) {
t.Parallel()
err := validateIdForName(tc.limit, tc.id)
if tc.err != "" {
test.AssertError(t, err, "should have failed")
test.AssertContains(t, err.Error(), tc.err)
} else {
test.AssertNotError(t, err, "should have succeeded")
}
})
}
}
func TestBuildBucketKey(t *testing.T) {
t.Parallel()
tests := []struct {
name Name
desc string
regId int64
singleIdent identifier.ACMEIdentifier
setOfIdents identifier.ACMEIdentifiers
subscriberIP netip.Addr
expectErrContains string
outputTest func(t *testing.T, key string)
}{
// NewRegistrationsPerIPAddress
{
name: NewRegistrationsPerIPAddress,
desc: "valid subscriber IPv4 address",
subscriberIP: netip.MustParseAddr("1.2.3.4"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1.2.3.4", NewRegistrationsPerIPAddress), key)
},
},
{
name: NewRegistrationsPerIPAddress,
desc: "valid subscriber IPv6 address",
subscriberIP: netip.MustParseAddr("2001:db8::1"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:2001:db8::1", NewRegistrationsPerIPAddress), key)
},
},
// NewRegistrationsPerIPv6Range
{
name: NewRegistrationsPerIPv6Range,
desc: "valid subscriber IPv6 address",
subscriberIP: netip.MustParseAddr("2001:db8:abcd:12::1"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:2001:db8:abcd::/48", NewRegistrationsPerIPv6Range), key)
},
},
{
name: NewRegistrationsPerIPv6Range,
desc: "subscriber IPv4 given for subscriber IPv6 range limit",
subscriberIP: netip.MustParseAddr("1.2.3.4"),
expectErrContains: "requires an IPv6 address",
},
// NewOrdersPerAccount
{
name: NewOrdersPerAccount,
desc: "valid registration ID",
regId: 1337,
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337", NewOrdersPerAccount), key)
},
},
{
name: NewOrdersPerAccount,
desc: "registration ID missing",
expectErrContains: "regId is required",
},
// CertificatesPerDomain
{
name: CertificatesPerDomain,
desc: "DNS identifier → eTLD+1",
singleIdent: identifier.FromString("www.example.com"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:example.com", CertificatesPerDomain), key)
},
},
{
name: CertificatesPerDomain,
desc: "valid IPv4 address used as identifier",
singleIdent: identifier.FromString("5.6.7.8"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:5.6.7.8/32", CertificatesPerDomain), key)
},
},
{
name: CertificatesPerDomain,
desc: "valid IPv6 address used as identifier",
singleIdent: identifier.FromString("2001:db8::1"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:2001:db8::/64", CertificatesPerDomain), key)
},
},
{
name: CertificatesPerDomain,
desc: "identifier missing",
expectErrContains: "singleIdent is required",
},
// CertificatesPerFQDNSet
{
name: CertificatesPerFQDNSet,
desc: "multiple valid DNS identifiers",
setOfIdents: identifier.FromStringSlice([]string{"example.com", "example.org"}),
outputTest: func(t *testing.T, key string) {
if !strings.HasPrefix(key, fmt.Sprintf("%d:", CertificatesPerFQDNSet)) {
t.Errorf("expected key to start with %d: got %s", CertificatesPerFQDNSet, key)
}
},
},
{
name: CertificatesPerFQDNSet,
desc: "multiple valid DNS and IP identifiers",
setOfIdents: identifier.FromStringSlice([]string{"example.net", "192.0.2.1", "2001:db8::1"}),
outputTest: func(t *testing.T, key string) {
if !strings.HasPrefix(key, fmt.Sprintf("%d:", CertificatesPerFQDNSet)) {
t.Errorf("expected key to start with %d: got %s", CertificatesPerFQDNSet, key)
}
},
},
{
name: CertificatesPerFQDNSet,
desc: "identifiers missing",
expectErrContains: "setOfIdents is required",
},
// CertificatesPerDomainPerAccount
{
name: CertificatesPerDomainPerAccount,
desc: "only registration ID",
regId: 1337,
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337", CertificatesPerDomainPerAccount), key)
},
},
{
name: CertificatesPerDomainPerAccount,
desc: "registration ID and single DNS identifier provided",
regId: 1337,
singleIdent: identifier.FromString("example.com"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337:example.com", CertificatesPerDomainPerAccount), key)
},
},
{
name: CertificatesPerDomainPerAccount,
desc: "single DNS identifier provided without registration ID",
singleIdent: identifier.FromString("example.com"),
expectErrContains: "regId is required",
},
// FailedAuthorizationsPerDomainPerAccount
{
name: FailedAuthorizationsPerDomainPerAccount,
desc: "registration ID and single DNS identifier",
regId: 1337,
singleIdent: identifier.FromString("example.com"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337:example.com", FailedAuthorizationsPerDomainPerAccount), key)
},
},
{
name: FailedAuthorizationsPerDomainPerAccount,
desc: "only registration ID",
regId: 1337,
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337", FailedAuthorizationsPerDomainPerAccount), key)
},
},
// FailedAuthorizationsForPausingPerDomainPerAccount
{
name: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "registration ID and single DNS identifier",
regId: 1337,
singleIdent: identifier.FromString("example.com"),
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337:example.com", FailedAuthorizationsForPausingPerDomainPerAccount), key)
},
},
{
name: FailedAuthorizationsForPausingPerDomainPerAccount,
desc: "only registration ID",
regId: 1337,
outputTest: func(t *testing.T, key string) {
test.AssertEquals(t, fmt.Sprintf("%d:1337", FailedAuthorizationsForPausingPerDomainPerAccount), key)
},
},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%s/%s", tc.name, tc.desc), func(t *testing.T) {
t.Parallel()
key, err := BuildBucketKey(tc.name, tc.regId, tc.singleIdent, tc.setOfIdents, tc.subscriberIP)
if tc.expectErrContains != "" {
test.AssertError(t, err, "expected error")
test.AssertContains(t, err.Error(), tc.expectErrContains)
return
}
test.AssertNotError(t, err, "unexpected error")
tc.outputTest(t, key)
})
}
}