forked from TrueCloudLab/certificates
acme/retry: Update DNS challenge tests
This commit is contained in:
parent
0f63e43b10
commit
d54f963b81
2 changed files with 181 additions and 261 deletions
|
@ -402,7 +402,7 @@ func (hc *http01Challenge) validate(jwk *jose.JSONWebKey, vo validateOptions) (c
|
||||||
// fail
|
// fail
|
||||||
up.Status = StatusInvalid
|
up.Status = StatusInvalid
|
||||||
e := errors.Errorf("keyAuthorization does not match; expected %s, but got %s", expected, keyAuth)
|
e := errors.Errorf("keyAuthorization does not match; expected %s, but got %s", expected, keyAuth)
|
||||||
up.Error = RejectedIdentifierErr(e).ToACME()
|
up.Error = IncorrectResponseErr(e).ToACME()
|
||||||
up.Retry = nil
|
up.Retry = nil
|
||||||
return up, nil
|
return up, nil
|
||||||
}
|
}
|
||||||
|
@ -596,7 +596,7 @@ func (dc *dns01Challenge) validate(jwk *jose.JSONWebKey, vo validateOptions) (ch
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := errors.Wrapf(err, "error looking up TXT records for domain %s", domain)
|
e := errors.Wrapf(err, "error looking up TXT records for domain %s", domain)
|
||||||
up.Error = DNSErr(e).ToACME()
|
up.Error = DNSErr(e).ToACME()
|
||||||
return dc, nil
|
return up, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedKeyAuth, err := KeyAuthorization(dc.Token, jwk)
|
expectedKeyAuth, err := KeyAuthorization(dc.Token, jwk)
|
||||||
|
@ -614,7 +614,7 @@ func (dc *dns01Challenge) validate(jwk *jose.JSONWebKey, vo validateOptions) (ch
|
||||||
|
|
||||||
for _, r := range txtRecords {
|
for _, r := range txtRecords {
|
||||||
if r == expected {
|
if r == expected {
|
||||||
up.Validated = time.Now().UTC()
|
up.Validated = clock.Now()
|
||||||
up.Status = StatusValid
|
up.Status = StatusValid
|
||||||
up.Error = nil
|
up.Error = nil
|
||||||
up.Retry = nil
|
up.Retry = nil
|
||||||
|
|
|
@ -791,24 +791,24 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
}
|
}
|
||||||
tests := map[string]func(t *testing.T) test{
|
tests := map[string]func(t *testing.T) test{
|
||||||
|
|
||||||
"ok/status-already-valid": func(t *testing.T) test {
|
"valid/status-noop": func(t *testing.T) test {
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
_ch, ok := ch.(*http01Challenge)
|
b := ch.clone()
|
||||||
assert.Fatal(t, ok)
|
b.Status = StatusValid
|
||||||
_ch.baseChallenge.Status = StatusValid
|
ch = b.morph()
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
res: ch,
|
res: ch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"ok/status-already-invalid": func(t *testing.T) test {
|
"invalid/status-noop": func(t *testing.T) test {
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
_ch, ok := ch.(*http01Challenge)
|
b := ch.clone()
|
||||||
assert.Fatal(t, ok)
|
b.Status = StatusInvalid
|
||||||
_ch.baseChallenge.Status = StatusInvalid
|
ch = b.morph()
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
res: ch,
|
res: ch,
|
||||||
|
@ -842,18 +842,9 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
"ok/http-get-error": func(t *testing.T) test {
|
"ok/http-get-error": func(t *testing.T) test {
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
up := ch.clone()
|
b := ch.clone()
|
||||||
up.Retry = &Retry{
|
b.Status = StatusProcessing
|
||||||
Owner: 0,
|
ch = b.morph()
|
||||||
ProvisionerID: "acme/acme",
|
|
||||||
NumAttempts: 1,
|
|
||||||
MaxAttempts: 6,
|
|
||||||
NextAttempt: time.Now().UTC().Add(time.Minute).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
up.Status = StatusProcessing
|
|
||||||
ch = up.morph()
|
|
||||||
chb, err := json.Marshal(ch)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
rch := ch.clone()
|
rch := ch.clone()
|
||||||
geterr := errors.New("force")
|
geterr := errors.New("force")
|
||||||
|
@ -868,29 +859,16 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
return nil, geterr
|
return nil, geterr
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
Ret1: chb,
|
|
||||||
Ret2: true,
|
|
||||||
},
|
|
||||||
res: rch,
|
res: rch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"ok/http-get->=400": func(t *testing.T) test {
|
"processing/http-get->=400": func(t *testing.T) test {
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
up := ch.clone()
|
b := ch.clone()
|
||||||
up.Retry = &Retry{
|
b.Status = StatusProcessing
|
||||||
Owner: 0,
|
ch = b.morph()
|
||||||
ProvisionerID: "acme/acme",
|
|
||||||
NumAttempts: 1,
|
|
||||||
MaxAttempts: 6,
|
|
||||||
NextAttempt: time.Now().UTC().Add(time.Minute).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
up.Status = StatusProcessing
|
|
||||||
ch = up.morph()
|
|
||||||
chb, err := json.Marshal(ch)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
rch := ch.clone()
|
rch := ch.clone()
|
||||||
url := fmt.Sprintf("http://%s/.well-known/acme-challenge/%s", ch.getValue(), ch.getToken())
|
url := fmt.Sprintf("http://%s/.well-known/acme-challenge/%s", ch.getValue(), ch.getToken())
|
||||||
|
@ -907,29 +885,16 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
Ret1: chb,
|
|
||||||
Ret2: true,
|
|
||||||
},
|
|
||||||
res: rch,
|
res: rch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"fail/read-body": func(t *testing.T) test {
|
"processing/read-body-error": func(t *testing.T) test {
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
up := ch.clone()
|
b := ch.clone()
|
||||||
up.Retry = &Retry{
|
b.Status = StatusProcessing
|
||||||
Owner: 0,
|
ch = b.morph()
|
||||||
ProvisionerID: "acme/acme",
|
|
||||||
NumAttempts: 1,
|
|
||||||
MaxAttempts: 6,
|
|
||||||
NextAttempt: time.Now().UTC().Add(time.Minute).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
up.Status = StatusProcessing
|
|
||||||
ch = up.morph()
|
|
||||||
chb, err := json.Marshal(ch)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
rch := ch.clone()
|
rch := ch.clone()
|
||||||
url := fmt.Sprintf("http://%s/.well-known/acme-challenge/%s", ch.getValue(), ch.getToken())
|
url := fmt.Sprintf("http://%s/.well-known/acme-challenge/%s", ch.getValue(), ch.getToken())
|
||||||
|
@ -945,33 +910,20 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
Ret1: chb,
|
|
||||||
Ret2: true,
|
|
||||||
},
|
|
||||||
res: rch,
|
res: rch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"fail/key-authorization-gen-error": func(t *testing.T) test {
|
"error/key-authorization-gen": func(t *testing.T) test {
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
jwk.Key = "foo"
|
|
||||||
|
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
b := ch.clone()
|
b := ch.clone()
|
||||||
b.Retry = &Retry{
|
|
||||||
Owner: 0,
|
|
||||||
ProvisionerID: "acme/acme",
|
|
||||||
NumAttempts: 1,
|
|
||||||
MaxAttempts: 6,
|
|
||||||
NextAttempt: time.Now().UTC().Add(time.Minute).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
b.Status = StatusProcessing
|
b.Status = StatusProcessing
|
||||||
ch = b.morph()
|
ch = b.morph()
|
||||||
chb, err := json.Marshal(ch)
|
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
jwk.Key = "foo"
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
|
@ -982,44 +934,28 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
Ret1: chb,
|
|
||||||
Ret2: true,
|
|
||||||
},
|
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
err: ServerInternalErr(errors.New("error generating JWK thumbprint: square/go-jose: unknown key type 'string'")),
|
err: ServerInternalErr(errors.New("error generating JWK thumbprint: square/go-jose: unknown key type 'string'")),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"ok/key-auth-mismatch": func(t *testing.T) test {
|
"invalid/key-auth-mismatch": func(t *testing.T) test {
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
b := ch.clone()
|
b := ch.clone()
|
||||||
b.Retry = &Retry{
|
|
||||||
Owner: 0,
|
|
||||||
ProvisionerID: "acme/acme",
|
|
||||||
NumAttempts: 1,
|
|
||||||
MaxAttempts: 6,
|
|
||||||
NextAttempt: time.Now().UTC().Add(time.Minute).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
b.Status = StatusProcessing
|
b.Status = StatusProcessing
|
||||||
ch = b.morph()
|
ch = b.morph()
|
||||||
chb, err := json.Marshal(ch)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
b = ch.clone()
|
b = ch.clone()
|
||||||
e := errors.Errorf("keyAuthorization does not match; expected %s, but got foo", expKeyAuth)
|
e := errors.Errorf("keyAuthorization does not match; expected %s, but got foo", expKeyAuth)
|
||||||
ae := RejectedIdentifierErr(e)
|
b.Error = IncorrectResponseErr(e).ToACME()
|
||||||
b.Error = ae.ToACME()
|
|
||||||
b.Retry = nil
|
b.Retry = nil
|
||||||
b.Status = StatusInvalid
|
b.Status = StatusInvalid
|
||||||
|
|
||||||
rch := b.morph()
|
rch := b.morph()
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
|
@ -1032,33 +968,19 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
Ret1: chb,
|
|
||||||
Ret2: true,
|
|
||||||
},
|
|
||||||
res: rch,
|
res: rch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"ok": func(t *testing.T) test {
|
"valid/normal-http-get": func(t *testing.T) test {
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
ch, err := newHTTPCh()
|
ch, err := newHTTPCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
b := ch.clone()
|
b := ch.clone()
|
||||||
b.Retry = &Retry{
|
|
||||||
Owner: 0,
|
|
||||||
ProvisionerID: "acme/acme",
|
|
||||||
NumAttempts: 1,
|
|
||||||
MaxAttempts: 6,
|
|
||||||
NextAttempt: time.Now().UTC().Add(time.Minute).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
b.Status = StatusProcessing
|
b.Status = StatusProcessing
|
||||||
ch = b.morph()
|
ch = b.morph()
|
||||||
chb, err := json.Marshal(ch)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
@ -1079,10 +1001,6 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
Ret1: chb,
|
|
||||||
Ret2: true,
|
|
||||||
},
|
|
||||||
res: rch,
|
res: rch,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1108,9 +1026,12 @@ func TestHTTP01Validate(t *testing.T) {
|
||||||
assert.Equals(t, tc.res.getToken(), ch.getToken())
|
assert.Equals(t, tc.res.getToken(), ch.getToken())
|
||||||
assert.Equals(t, tc.res.getCreated(), ch.getCreated())
|
assert.Equals(t, tc.res.getCreated(), ch.getCreated())
|
||||||
if tc.res.getValidated() != ch.getValidated() {
|
if tc.res.getValidated() != ch.getValidated() {
|
||||||
assert.True(t, ch.getValidated().After(tc.res.getValidated()),
|
now := clock.Now()
|
||||||
"validated timestamp should come after challenge creation")
|
window := now.Sub(tc.res.getValidated())
|
||||||
|
assert.True(t, now.Sub(ch.getValidated()) <= window,
|
||||||
|
"validated timestamp should come before now but after test case setup")
|
||||||
|
} else {
|
||||||
|
assert.Equals(t, tc.res.getValidated(), ch.getValidated())
|
||||||
}
|
}
|
||||||
assert.Equals(t, tc.res.getError(), ch.getError())
|
assert.Equals(t, tc.res.getError(), ch.getError())
|
||||||
assert.Equals(t, tc.res.getRetry(), ch.getRetry())
|
assert.Equals(t, tc.res.getRetry(), ch.getRetry())
|
||||||
|
@ -1845,42 +1766,67 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
err *Error
|
err *Error
|
||||||
}
|
}
|
||||||
tests := map[string]func(t *testing.T) test{
|
tests := map[string]func(t *testing.T) test{
|
||||||
"ok/status-already-valid": func(t *testing.T) test {
|
|
||||||
ch, err := newDNSCh()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_ch, ok := ch.(*dns01Challenge)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_ch.baseChallenge.Status = StatusValid
|
|
||||||
return test{
|
|
||||||
ch: ch,
|
|
||||||
res: ch,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/status-already-invalid": func(t *testing.T) test {
|
|
||||||
ch, err := newDNSCh()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_ch, ok := ch.(*dns01Challenge)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_ch.baseChallenge.Status = StatusInvalid
|
|
||||||
return test{
|
|
||||||
ch: ch,
|
|
||||||
res: ch,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ok/lookup-txt-error": func(t *testing.T) test {
|
|
||||||
ch, err := newDNSCh()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
oldb, err := json.Marshal(ch)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
expErr := DNSErr(errors.Errorf("error looking up TXT records for "+
|
"valid/status-noop": func(t *testing.T) test {
|
||||||
"domain %s: force", ch.getValue()))
|
ch, err := newDNSCh()
|
||||||
baseClone := ch.clone()
|
|
||||||
baseClone.Error = expErr.ToACME()
|
|
||||||
baseClone.Error.Subproblems = append(baseClone.Error.Subproblems, expErr)
|
|
||||||
newCh := &dns01Challenge{baseClone}
|
|
||||||
newb, err := json.Marshal(newCh)
|
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusValid
|
||||||
|
ch = b.morph()
|
||||||
|
return test{
|
||||||
|
ch: ch,
|
||||||
|
res: ch,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"invalid/status-noop": func(t *testing.T) test {
|
||||||
|
ch, err := newDNSCh()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusInvalid
|
||||||
|
ch = b.morph()
|
||||||
|
return test{
|
||||||
|
ch: ch,
|
||||||
|
res: ch,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"error/status-pending": func(t *testing.T) test {
|
||||||
|
ch, err := newDNSCh()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusPending
|
||||||
|
e := errors.New("pending challenges must first be moved to the processing state")
|
||||||
|
return test{
|
||||||
|
ch: b.morph(),
|
||||||
|
err: ServerInternalErr(e),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"error/status-unknown": func(t *testing.T) test {
|
||||||
|
ch, err := newDNSCh()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = "unknown"
|
||||||
|
e := errors.New("unknown challenge state: unknown")
|
||||||
|
return test{
|
||||||
|
ch: b.morph(),
|
||||||
|
err: ServerInternalErr(e),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"processing/lookup-txt-error": func(t *testing.T) test {
|
||||||
|
ch, err := newDNSCh()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusProcessing
|
||||||
|
ch = b.morph()
|
||||||
|
|
||||||
|
b = ch.clone()
|
||||||
|
e := errors.Errorf("error looking up TXT records for domain %s: force", ch.getValue())
|
||||||
|
b.Error = DNSErr(e).ToACME()
|
||||||
|
rch := b.morph()
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
vo: validateOptions{
|
vo: validateOptions{
|
||||||
|
@ -1888,65 +1834,21 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
return nil, errors.New("force")
|
return nil, errors.New("force")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
db: &db.MockNoSQLDB{
|
res: rch,
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
assert.Equals(t, bucket, challengeTable)
|
|
||||||
assert.Equals(t, key, []byte(ch.getID()))
|
|
||||||
assert.Equals(t, old, oldb)
|
|
||||||
assert.Equals(t, newval, newb)
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
res: ch,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok/lookup-txt-wildcard": func(t *testing.T) test {
|
|
||||||
ch, err := newDNSCh()
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
_ch, ok := ch.(*dns01Challenge)
|
|
||||||
assert.Fatal(t, ok)
|
|
||||||
_ch.baseChallenge.Value = "*.zap.internal"
|
|
||||||
|
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
h := sha256.Sum256([]byte(expKeyAuth))
|
|
||||||
expected := base64.RawURLEncoding.EncodeToString(h[:])
|
|
||||||
|
|
||||||
baseClone := ch.clone()
|
|
||||||
baseClone.Status = StatusValid
|
|
||||||
baseClone.Error = nil
|
|
||||||
newCh := &dns01Challenge{baseClone}
|
|
||||||
|
|
||||||
return test{
|
|
||||||
ch: ch,
|
|
||||||
res: newCh,
|
|
||||||
vo: validateOptions{
|
|
||||||
lookupTxt: func(url string) ([]string, error) {
|
|
||||||
assert.Equals(t, url, "_acme-challenge.zap.internal")
|
|
||||||
return []string{"foo", expected}, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
jwk: jwk,
|
|
||||||
db: &db.MockNoSQLDB{
|
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
dnsCh, err := unmarshalChallenge(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
assert.Equals(t, dnsCh.getStatus(), StatusValid)
|
|
||||||
baseClone.Validated = dnsCh.getValidated()
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fail/key-authorization-gen-error": func(t *testing.T) test {
|
"fail/key-authorization-gen-error": func(t *testing.T) test {
|
||||||
ch, err := newDNSCh()
|
ch, err := newDNSCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusProcessing
|
||||||
|
ch = b.morph()
|
||||||
|
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
jwk.Key = "foo"
|
jwk.Key = "foo"
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
vo: validateOptions{
|
vo: validateOptions{
|
||||||
|
@ -1958,26 +1860,25 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
err: ServerInternalErr(errors.New("error generating JWK thumbprint: square/go-jose: unknown key type 'string'")),
|
err: ServerInternalErr(errors.New("error generating JWK thumbprint: square/go-jose: unknown key type 'string'")),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok/key-auth-mismatch": func(t *testing.T) test {
|
|
||||||
|
"invalid/key-auth-mismatch": func(t *testing.T) test {
|
||||||
ch, err := newDNSCh()
|
ch, err := newDNSCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
oldb, err := json.Marshal(ch)
|
b := ch.clone()
|
||||||
assert.FatalError(t, err)
|
b.Status = StatusProcessing
|
||||||
|
ch = b.morph()
|
||||||
|
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
expErr := RejectedIdentifierErr(errors.Errorf("keyAuthorization does not match; "+
|
e := errors.Errorf("keyAuthorization does not match; "+
|
||||||
"expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}))
|
"expected %s, but got %s", expKeyAuth, []string{"foo", "bar"})
|
||||||
baseClone := ch.clone()
|
b = ch.clone()
|
||||||
baseClone.Error = expErr.ToACME()
|
b.Status = StatusInvalid
|
||||||
baseClone.Error.Subproblems = append(baseClone.Error.Subproblems, expErr)
|
b.Error = IncorrectResponseErr(e).ToACME()
|
||||||
newCh := &http01Challenge{baseClone}
|
rch := b.morph()
|
||||||
newb, err := json.Marshal(newCh)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
|
@ -1987,29 +1888,58 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
db: &db.MockNoSQLDB{
|
res: rch,
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
assert.Equals(t, bucket, challengeTable)
|
|
||||||
assert.Equals(t, key, []byte(ch.getID()))
|
|
||||||
assert.Equals(t, old, oldb)
|
|
||||||
assert.Equals(t, newval, newb)
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
res: ch,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fail/save-error": func(t *testing.T) test {
|
|
||||||
|
"processing/empty-list": func(t *testing.T) test {
|
||||||
ch, err := newDNSCh()
|
ch, err := newDNSCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusProcessing
|
||||||
|
ch = b.morph()
|
||||||
|
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
|
||||||
|
e := errors.New("no TXT record found at '_acme-challenge.zap.internal'")
|
||||||
|
b = ch.clone()
|
||||||
|
b.Error = DNSErr(e).ToACME()
|
||||||
|
rch := b.morph()
|
||||||
|
|
||||||
|
return test{
|
||||||
|
ch: ch,
|
||||||
|
vo: validateOptions{
|
||||||
|
lookupTxt: func(url string) ([]string, error) {
|
||||||
|
return []string{}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
jwk: jwk,
|
||||||
|
res: rch,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"valid/lookup-txt-normal": func(t *testing.T) test {
|
||||||
|
ch, err := newDNSCh()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
b := ch.clone()
|
||||||
|
b.Status = StatusProcessing
|
||||||
|
ch = b.morph()
|
||||||
|
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
h := sha256.Sum256([]byte(expKeyAuth))
|
h := sha256.Sum256([]byte(expKeyAuth))
|
||||||
expected := base64.RawURLEncoding.EncodeToString(h[:])
|
expected := base64.RawURLEncoding.EncodeToString(h[:])
|
||||||
|
|
||||||
|
b = ch.clone()
|
||||||
|
b.Validated = clock.Now()
|
||||||
|
b.Status = StatusValid
|
||||||
|
b.Error = nil
|
||||||
|
b.Retry = nil
|
||||||
|
rch := b.morph()
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
vo: validateOptions{
|
vo: validateOptions{
|
||||||
|
@ -2018,22 +1948,17 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
db: &db.MockNoSQLDB{
|
res: rch,
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
return nil, false, errors.New("force")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
err: ServerInternalErr(errors.New("error saving acme challenge: force")),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok": func(t *testing.T) test {
|
|
||||||
|
"valid/lookup-txt-wildcard": func(t *testing.T) test {
|
||||||
ch, err := newDNSCh()
|
ch, err := newDNSCh()
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
_ch, ok := ch.(*dns01Challenge)
|
b := ch.clone()
|
||||||
assert.Fatal(t, ok)
|
b.Status = StatusProcessing
|
||||||
_ch.baseChallenge.Error = MalformedErr(nil).ToACME()
|
b.Value = "*.zap.internal"
|
||||||
oldb, err := json.Marshal(ch)
|
ch = b.morph()
|
||||||
assert.FatalError(t, err)
|
|
||||||
|
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
@ -2043,40 +1968,27 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
h := sha256.Sum256([]byte(expKeyAuth))
|
h := sha256.Sum256([]byte(expKeyAuth))
|
||||||
expected := base64.RawURLEncoding.EncodeToString(h[:])
|
expected := base64.RawURLEncoding.EncodeToString(h[:])
|
||||||
|
|
||||||
baseClone := ch.clone()
|
b = ch.clone()
|
||||||
baseClone.Status = StatusValid
|
b.Status = StatusValid
|
||||||
baseClone.Error = nil
|
b.Validated = clock.Now()
|
||||||
newCh := &dns01Challenge{baseClone}
|
b.Error = nil
|
||||||
|
b.Retry = nil
|
||||||
|
rch := b.morph()
|
||||||
|
|
||||||
return test{
|
return test{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
res: newCh,
|
|
||||||
vo: validateOptions{
|
vo: validateOptions{
|
||||||
lookupTxt: func(url string) ([]string, error) {
|
lookupTxt: func(url string) ([]string, error) {
|
||||||
|
assert.Equals(t, url, "_acme-challenge.zap.internal")
|
||||||
return []string{"foo", expected}, nil
|
return []string{"foo", expected}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
db: &db.MockNoSQLDB{
|
res: rch,
|
||||||
MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) {
|
|
||||||
assert.Equals(t, bucket, challengeTable)
|
|
||||||
assert.Equals(t, key, []byte(ch.getID()))
|
|
||||||
assert.Equals(t, old, oldb)
|
|
||||||
|
|
||||||
dnsCh, err := unmarshalChallenge(newval)
|
|
||||||
assert.FatalError(t, err)
|
|
||||||
assert.Equals(t, dnsCh.getStatus(), StatusValid)
|
|
||||||
assert.True(t, dnsCh.getValidated().Before(time.Now().UTC()))
|
|
||||||
assert.True(t, dnsCh.getValidated().After(time.Now().UTC().Add(-1*time.Second)))
|
|
||||||
|
|
||||||
baseClone.Validated = dnsCh.getValidated()
|
|
||||||
|
|
||||||
return nil, true, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, run := range tests {
|
for name, run := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tc := run(t)
|
tc := run(t)
|
||||||
|
@ -2096,8 +2008,16 @@ func TestDNS01Validate(t *testing.T) {
|
||||||
assert.Equals(t, tc.res.getStatus(), ch.getStatus())
|
assert.Equals(t, tc.res.getStatus(), ch.getStatus())
|
||||||
assert.Equals(t, tc.res.getToken(), ch.getToken())
|
assert.Equals(t, tc.res.getToken(), ch.getToken())
|
||||||
assert.Equals(t, tc.res.getCreated(), ch.getCreated())
|
assert.Equals(t, tc.res.getCreated(), ch.getCreated())
|
||||||
assert.Equals(t, tc.res.getValidated(), ch.getValidated())
|
if tc.res.getValidated() != ch.getValidated() {
|
||||||
|
now := clock.Now()
|
||||||
|
window := now.Sub(tc.res.getValidated())
|
||||||
|
assert.True(t, now.Sub(ch.getValidated()) <= window,
|
||||||
|
"validated timestamp should come before now but after test case setup")
|
||||||
|
} else {
|
||||||
|
assert.Equals(t, tc.res.getValidated(), ch.getValidated())
|
||||||
|
}
|
||||||
assert.Equals(t, tc.res.getError(), ch.getError())
|
assert.Equals(t, tc.res.getError(), ch.getError())
|
||||||
|
assert.Equals(t, tc.res.getRetry(), ch.getRetry())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue