186 lines
6.7 KiB
Go
186 lines
6.7 KiB
Go
//go:build integration
|
||
|
||
package integration
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/eggsampler/acme/v3"
|
||
|
||
"github.com/letsencrypt/boulder/test"
|
||
)
|
||
|
||
// TestTooBigOrderError tests that submitting an order with more than 100 names
|
||
// produces the expected problem result.
|
||
func TestTooBigOrderError(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
var domains []string
|
||
for i := range 101 {
|
||
domains = append(domains, fmt.Sprintf("%d.example.com", i))
|
||
}
|
||
|
||
_, err := authAndIssue(nil, nil, domains, true)
|
||
test.AssertError(t, err, "authAndIssue failed")
|
||
|
||
var prob acme.Problem
|
||
test.AssertErrorWraps(t, err, &prob)
|
||
test.AssertEquals(t, prob.Type, "urn:ietf:params:acme:error:malformed")
|
||
test.AssertEquals(t, prob.Detail, "Order cannot contain more than 100 DNS names")
|
||
}
|
||
|
||
// TestAccountEmailError tests that registering a new account, or updating an
|
||
// account, with invalid contact information produces the expected problem
|
||
// result to ACME clients.
|
||
func TestAccountEmailError(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
// The registrations.contact field is VARCHAR(191). 175 'a' characters plus
|
||
// the prefix "mailto:" and the suffix "@a.com" makes exactly 191 bytes of
|
||
// encoded JSON. The correct size to hit our maximum DB field length.
|
||
var longStringBuf strings.Builder
|
||
longStringBuf.WriteString("mailto:")
|
||
for range 175 {
|
||
longStringBuf.WriteRune('a')
|
||
}
|
||
longStringBuf.WriteString("@a.com")
|
||
|
||
createErrorPrefix := "Error creating new account :: "
|
||
updateErrorPrefix := "Unable to update account :: "
|
||
|
||
testCases := []struct {
|
||
name string
|
||
contacts []string
|
||
expectedProbType string
|
||
expectedProbDetail string
|
||
}{
|
||
{
|
||
name: "empty contact",
|
||
contacts: []string{"mailto:valid@valid.com", ""},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: `empty contact`,
|
||
},
|
||
{
|
||
name: "empty proto",
|
||
contacts: []string{"mailto:valid@valid.com", " "},
|
||
expectedProbType: "urn:ietf:params:acme:error:unsupportedContact",
|
||
expectedProbDetail: `contact method "" is not supported`,
|
||
},
|
||
{
|
||
name: "empty mailto",
|
||
contacts: []string{"mailto:valid@valid.com", "mailto:"},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: `"" is not a valid e-mail address`,
|
||
},
|
||
{
|
||
name: "non-ascii mailto",
|
||
contacts: []string{"mailto:valid@valid.com", "mailto:cpu@l̴etsencrypt.org"},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: `contact email ["mailto:cpu@l̴etsencrypt.org"] contains non-ASCII characters`,
|
||
},
|
||
{
|
||
name: "too many contacts",
|
||
contacts: []string{"a", "b", "c", "d"},
|
||
expectedProbType: "urn:ietf:params:acme:error:malformed",
|
||
expectedProbDetail: `too many contacts provided: 4 > 3`,
|
||
},
|
||
{
|
||
name: "invalid contact",
|
||
contacts: []string{"mailto:valid@valid.com", "mailto:a@"},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: `"a@" is not a valid e-mail address`,
|
||
},
|
||
{
|
||
name: "forbidden contact domain",
|
||
contacts: []string{"mailto:valid@valid.com", "mailto:a@example.com"},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: "invalid contact domain. Contact emails @example.com are forbidden",
|
||
},
|
||
{
|
||
name: "contact domain invalid TLD",
|
||
contacts: []string{"mailto:valid@valid.com", "mailto:a@example.cpu"},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: `contact email "a@example.cpu" has invalid domain : Domain name does not end with a valid public suffix (TLD)`,
|
||
},
|
||
{
|
||
name: "contact domain invalid",
|
||
contacts: []string{"mailto:valid@valid.com", "mailto:a@example./.com"},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: "contact email \"a@example./.com\" has invalid domain : Domain name contains an invalid character",
|
||
},
|
||
{
|
||
name: "too long contact",
|
||
contacts: []string{
|
||
longStringBuf.String(),
|
||
},
|
||
expectedProbType: "urn:ietf:params:acme:error:invalidContact",
|
||
expectedProbDetail: `too many/too long contact(s). Please use shorter or fewer email addresses`,
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
// First try registering a new account and ensuring the expected problem occurs
|
||
var prob acme.Problem
|
||
_, err := makeClient(tc.contacts...)
|
||
if err != nil {
|
||
test.AssertErrorWraps(t, err, &prob)
|
||
test.AssertEquals(t, prob.Type, tc.expectedProbType)
|
||
test.AssertEquals(t, prob.Detail, createErrorPrefix+tc.expectedProbDetail)
|
||
} else {
|
||
t.Errorf("expected %s type problem for %q, got nil",
|
||
tc.expectedProbType, strings.Join(tc.contacts, ","))
|
||
}
|
||
|
||
// Next try making a client with a good contact and updating with the test
|
||
// case contact info. The same problem should occur.
|
||
c, err := makeClient("mailto:valid@valid.com")
|
||
test.AssertNotError(t, err, "failed to create account with valid contact")
|
||
_, err = c.UpdateAccount(c.Account, tc.contacts...)
|
||
if err != nil {
|
||
test.AssertErrorWraps(t, err, &prob)
|
||
test.AssertEquals(t, prob.Type, tc.expectedProbType)
|
||
test.AssertEquals(t, prob.Detail, updateErrorPrefix+tc.expectedProbDetail)
|
||
} else {
|
||
t.Errorf("expected %s type problem after updating account to %q, got nil",
|
||
tc.expectedProbType, strings.Join(tc.contacts, ","))
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestRejectedIdentifier(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
// When a single malformed name is provided, we correctly reject it.
|
||
domains := []string{
|
||
"яџ–Х6яяdь}",
|
||
}
|
||
_, err := authAndIssue(nil, nil, domains, true)
|
||
test.AssertError(t, err, "issuance should fail for one malformed name")
|
||
var prob acme.Problem
|
||
test.AssertErrorWraps(t, err, &prob)
|
||
test.AssertEquals(t, prob.Type, "urn:ietf:params:acme:error:rejectedIdentifier")
|
||
test.AssertContains(t, prob.Detail, "Domain name contains an invalid character")
|
||
|
||
// When multiple malformed names are provided, we correctly reject all of
|
||
// them and reflect this in suberrors. This test ensures that the way we
|
||
// encode these errors across the gRPC boundary is resilient to non-ascii
|
||
// characters.
|
||
domains = []string{
|
||
"o-",
|
||
"ш№Ў",
|
||
"р±y",
|
||
"яџ–Х6яя",
|
||
"яџ–Х6яя`ь",
|
||
}
|
||
_, err = authAndIssue(nil, nil, domains, true)
|
||
test.AssertError(t, err, "issuance should fail for multiple malformed names")
|
||
test.AssertErrorWraps(t, err, &prob)
|
||
test.AssertEquals(t, prob.Type, "urn:ietf:params:acme:error:rejectedIdentifier")
|
||
test.AssertContains(t, prob.Detail, "Domain name contains an invalid character")
|
||
test.AssertContains(t, prob.Detail, "and 4 more problems")
|
||
}
|