mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
refactor: simplify record matching
This commit is contained in:
parent
1b6962633f
commit
e263ebd77c
3 changed files with 50 additions and 45 deletions
|
|
@ -170,36 +170,24 @@ func (c *Challenge) selectIssuerDomainName(challIssuers []string, records []TXTR
|
|||
}
|
||||
|
||||
func (c *Challenge) hasMatchingRecord(records []TXTRecord, issuerDomainName string, wildcard bool) bool {
|
||||
for _, record := range records {
|
||||
parsed, err := parseIssueValue(record.Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if parsed.IssuerDomainName != issuerDomainName {
|
||||
continue
|
||||
}
|
||||
|
||||
if parsed.AccountURI != c.accountURI {
|
||||
continue
|
||||
}
|
||||
|
||||
if wildcard && !strings.EqualFold(parsed.Policy, policyWildcard) {
|
||||
continue
|
||||
}
|
||||
|
||||
if c.persistUntil.IsZero() {
|
||||
if !parsed.PersistUntil.IsZero() {
|
||||
continue
|
||||
}
|
||||
} else if parsed.PersistUntil.IsZero() || !parsed.PersistUntil.Equal(c.persistUntil) {
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
iv := IssueValue{
|
||||
IssuerDomainName: issuerDomainName,
|
||||
AccountURI: c.accountURI,
|
||||
PersistUntil: c.persistUntil,
|
||||
}
|
||||
|
||||
return false
|
||||
if wildcard {
|
||||
iv.Policy = policyWildcard
|
||||
}
|
||||
|
||||
return slices.ContainsFunc(records, func(record TXTRecord) bool {
|
||||
parsed, err := parseIssueValue(record.Value)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return parsed.match(iv)
|
||||
})
|
||||
}
|
||||
|
||||
// GetAuthorizationDomainName returns the fully qualified DNS label
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package dnspersist01
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
|
@ -23,6 +24,22 @@ type IssueValue struct {
|
|||
PersistUntil time.Time
|
||||
}
|
||||
|
||||
func (v *IssueValue) match(other IssueValue) bool {
|
||||
if cmp.Or(
|
||||
cmp.Compare(v.IssuerDomainName, other.IssuerDomainName),
|
||||
cmp.Compare(v.AccountURI, other.AccountURI),
|
||||
v.PersistUntil.Compare(other.PersistUntil),
|
||||
) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.EqualFold(other.Policy, policyWildcard) && !strings.EqualFold(v.Policy, policyWildcard) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// buildIssueValue constructs an RFC 8659 issue-value for a dns-persist-01 TXT record.
|
||||
// issuerDomainName and accountURI are required.
|
||||
// wildcard and persistUntil are optional.
|
||||
|
|
@ -54,15 +71,15 @@ func buildIssueValue(issuerDomainName, accountURI string, wildcard bool, persist
|
|||
// It returns an error if any portion of the value is malformed.
|
||||
//
|
||||
//nolint:gocyclo // parsing and validating tagged parameters requires branching
|
||||
func parseIssueValue(value string) (IssueValue, error) {
|
||||
func parseIssueValue(value string) (*IssueValue, error) {
|
||||
fields := strings.Split(value, ";")
|
||||
|
||||
issuerDomainName := trimWSP(fields[0])
|
||||
if issuerDomainName == "" {
|
||||
return IssueValue{}, errors.New("missing issuer-domain-name")
|
||||
return nil, errors.New("missing issuer-domain-name")
|
||||
}
|
||||
|
||||
parsed := IssueValue{
|
||||
parsed := &IssueValue{
|
||||
IssuerDomainName: issuerDomainName,
|
||||
}
|
||||
|
||||
|
|
@ -72,24 +89,24 @@ func parseIssueValue(value string) (IssueValue, error) {
|
|||
for _, raw := range fields[1:] {
|
||||
part := trimWSP(raw)
|
||||
if part == "" {
|
||||
return IssueValue{}, errors.New("empty parameter or trailing semicolon provided")
|
||||
return nil, errors.New("empty parameter or trailing semicolon provided")
|
||||
}
|
||||
|
||||
// Capture each tag=value pair.
|
||||
tag, val, found := strings.Cut(part, "=")
|
||||
if !found {
|
||||
return IssueValue{}, fmt.Errorf("malformed parameter %q should be tag=value pair", part)
|
||||
return nil, fmt.Errorf("malformed parameter %q should be tag=value pair", part)
|
||||
}
|
||||
|
||||
tag = trimWSP(tag)
|
||||
|
||||
if tag == "" {
|
||||
return IssueValue{}, fmt.Errorf("malformed parameter %q, empty tag", part)
|
||||
return nil, fmt.Errorf("malformed parameter %q, empty tag", part)
|
||||
}
|
||||
|
||||
canonicalTag := strings.ToLower(tag)
|
||||
if seenTags[canonicalTag] {
|
||||
return IssueValue{}, fmt.Errorf("duplicate parameter %q", tag)
|
||||
return nil, fmt.Errorf("duplicate parameter %q", tag)
|
||||
}
|
||||
|
||||
seenTags[canonicalTag] = true
|
||||
|
|
@ -102,7 +119,7 @@ func parseIssueValue(value string) (IssueValue, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
return IssueValue{}, fmt.Errorf("malformed value %q for tag %q", val, tag)
|
||||
return nil, fmt.Errorf("malformed value %q for tag %q", val, tag)
|
||||
}
|
||||
|
||||
// Finally, capture expected tag values.
|
||||
|
|
@ -111,7 +128,7 @@ func parseIssueValue(value string) (IssueValue, error) {
|
|||
switch canonicalTag {
|
||||
case paramAccountURI:
|
||||
if val == "" {
|
||||
return IssueValue{}, fmt.Errorf("empty value provided for mandatory %q", paramAccountURI)
|
||||
return nil, fmt.Errorf("empty value provided for mandatory %q", paramAccountURI)
|
||||
}
|
||||
|
||||
parsed.AccountURI = val
|
||||
|
|
@ -130,7 +147,7 @@ func parseIssueValue(value string) (IssueValue, error) {
|
|||
case paramPersistUntil:
|
||||
ts, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return IssueValue{}, fmt.Errorf("malformed %q: %w", paramPersistUntil, err)
|
||||
return nil, fmt.Errorf("malformed %q: %w", paramPersistUntil, err)
|
||||
}
|
||||
|
||||
parsed.PersistUntil = time.Unix(ts, 0).UTC()
|
||||
|
|
|
|||
|
|
@ -67,13 +67,13 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected IssueValue
|
||||
expected *IssueValue
|
||||
expectErrContains string
|
||||
}{
|
||||
{
|
||||
desc: "basic",
|
||||
value: "authority.example; accounturi=https://authority.example/acct/123",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
},
|
||||
|
|
@ -81,7 +81,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "wildcard policy is case-insensitive",
|
||||
value: "authority.example; accounturi=https://authority.example/acct/123; policy=wIlDcArD",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
Policy: "wIlDcArD",
|
||||
|
|
@ -90,7 +90,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "unknown param",
|
||||
value: "authority.example; accounturi=https://authority.example/acct/123; extra=value",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
},
|
||||
|
|
@ -98,7 +98,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "unknown tag with empty value",
|
||||
value: "authority.example; accounturi=https://authority.example/acct/123; foo=",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
},
|
||||
|
|
@ -106,7 +106,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "unknown tags with unusual formatting are ignored",
|
||||
value: "authority.example;accounturi=https://authority.example/acct/123;bad tag=value;\nweird=\\x01337",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
},
|
||||
|
|
@ -114,7 +114,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "all known fields with heavy whitespace",
|
||||
value: " authority.example ; accounturi = https://authority.example/acct/123 ; policy = wildcard ; persistUntil = 4102444800 ",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
Policy: "wildcard",
|
||||
|
|
@ -124,7 +124,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "policy other than wildcard is treated as absent",
|
||||
value: "authority.example; accounturi=https://authority.example/acct/123; policy=notwildcard",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
AccountURI: "https://authority.example/acct/123",
|
||||
},
|
||||
|
|
@ -132,7 +132,7 @@ func Test_parseIssueValue(t *testing.T) {
|
|||
{
|
||||
desc: "missing accounturi",
|
||||
value: "authority.example",
|
||||
expected: IssueValue{
|
||||
expected: &IssueValue{
|
||||
IssuerDomainName: "authority.example",
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue