Skip to content

Commit

Permalink
[acme db interface] unit tests for challenge nosql db
Browse files Browse the repository at this point in the history
  • Loading branch information
dopey committed Mar 25, 2021
1 parent 4b1dda5 commit 206909b
Show file tree
Hide file tree
Showing 9 changed files with 788 additions and 96 deletions.
4 changes: 2 additions & 2 deletions acme/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
type Authorization struct {
Identifier Identifier `json:"identifier"`
Status Status `json:"status"`
Expires time.Time `json:"expires"`
ExpiresAt time.Time `json:"expires"`
Challenges []*Challenge `json:"challenges"`
Wildcard bool `json:"wildcard"`
ID string `json:"-"`
Expand Down Expand Up @@ -39,7 +39,7 @@ func (az *Authorization) UpdateStatus(ctx context.Context, db DB) error {
return nil
case StatusPending:
// check expiry
if now.After(az.Expires) {
if now.After(az.ExpiresAt) {
az.Status = StatusInvalid
break
}
Expand Down
26 changes: 13 additions & 13 deletions acme/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ import (

// Challenge represents an ACME response Challenge type.
type Challenge struct {
Type string `json:"type"`
Status Status `json:"status"`
Token string `json:"token"`
Validated string `json:"validated,omitempty"`
URL string `json:"url"`
Error *Error `json:"error,omitempty"`
ID string `json:"-"`
AuthzID string `json:"-"`
AccountID string `json:"-"`
Value string `json:"-"`
Type string `json:"type"`
Status Status `json:"status"`
Token string `json:"token"`
ValidatedAt string `json:"validated,omitempty"`
URL string `json:"url"`
Error *Error `json:"error,omitempty"`
ID string `json:"-"`
AuthzID string `json:"-"`
AccountID string `json:"-"`
Value string `json:"-"`
}

// ToLog enables response logging.
Expand Down Expand Up @@ -97,7 +97,7 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb
// Update and store the challenge.
ch.Status = StatusValid
ch.Error = nil
ch.Validated = clock.Now().Format(time.RFC3339)
ch.ValidatedAt = clock.Now().Format(time.RFC3339)

if err = db.UpdateChallenge(ctx, ch); err != nil {
return WrapErrorISE(err, "error updating challenge")
Expand Down Expand Up @@ -175,7 +175,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON

ch.Status = StatusValid
ch.Error = nil
ch.Validated = clock.Now().Format(time.RFC3339)
ch.ValidatedAt = clock.Now().Format(time.RFC3339)

if err = db.UpdateChallenge(ctx, ch); err != nil {
return WrapErrorISE(err, "tlsalpn01ValidateChallenge - error updating challenge")
Expand Down Expand Up @@ -231,7 +231,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK
// Update and store the challenge.
ch.Status = StatusValid
ch.Error = nil
ch.Validated = clock.Now().UTC().Format(time.RFC3339)
ch.ValidatedAt = clock.Now().Format(time.RFC3339)

if err = db.UpdateChallenge(ctx, ch); err != nil {
return WrapErrorISE(err, "error updating challenge")
Expand Down
10 changes: 5 additions & 5 deletions acme/db/nosql/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ type dbAuthz struct {
AccountID string `json:"accountID"`
Identifier acme.Identifier `json:"identifier"`
Status acme.Status `json:"status"`
Expires time.Time `json:"expires"`
ExpiresAt time.Time `json:"expiresAt"`
Challenges []string `json:"challenges"`
Wildcard bool `json:"wildcard"`
Created time.Time `json:"created"`
CreatedAt time.Time `json:"createdAt"`
Error *acme.Error `json:"error"`
}

Expand Down Expand Up @@ -66,7 +66,7 @@ func (db *DB) GetAuthorization(ctx context.Context, id string) (*acme.Authorizat
Status: dbaz.Status,
Challenges: chs,
Wildcard: dbaz.Wildcard,
Expires: dbaz.Expires,
ExpiresAt: dbaz.ExpiresAt,
ID: dbaz.ID,
}, nil
}
Expand All @@ -90,8 +90,8 @@ func (db *DB) CreateAuthorization(ctx context.Context, az *acme.Authorization) e
ID: az.ID,
AccountID: az.AccountID,
Status: acme.StatusPending,
Created: now,
Expires: now.Add(defaultExpiryDuration),
CreatedAt: now,
ExpiresAt: now.Add(defaultExpiryDuration),
Identifier: az.Identifier,
Challenges: chIDs,
Wildcard: az.Wildcard,
Expand Down
6 changes: 3 additions & 3 deletions acme/db/nosql/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

type dbCert struct {
ID string `json:"id"`
Created time.Time `json:"created"`
CreatedAt time.Time `json:"createdAt"`
AccountID string `json:"accountID"`
OrderID string `json:"orderID"`
Leaf []byte `json:"leaf"`
Expand Down Expand Up @@ -47,7 +47,7 @@ func (db *DB) CreateCertificate(ctx context.Context, cert *acme.Certificate) err
OrderID: cert.OrderID,
Leaf: leaf,
Intermediates: intermediates,
Created: time.Now().UTC(),
CreatedAt: time.Now().UTC(),
}
return db.save(ctx, cert.ID, dbch, nil, "certificate", certTable)
}
Expand All @@ -57,7 +57,7 @@ func (db *DB) CreateCertificate(ctx context.Context, cert *acme.Certificate) err
func (db *DB) GetCertificate(ctx context.Context, id string) (*acme.Certificate, error) {
b, err := db.db.Get(certTable, []byte(id))
if nosql.IsErrNotFound(err) {
return nil, errors.Wrapf(err, "certificate %s not found", id)
return nil, acme.NewError(acme.ErrorMalformedType, "certificate %s not found", id)
} else if err != nil {
return nil, errors.Wrapf(err, "error loading certificate %s", id)
}
Expand Down
40 changes: 25 additions & 15 deletions acme/db/nosql/certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestDB_CreateCertificate(t *testing.T) {
var tests = map[string]func(t *testing.T) test{
"fail/cmpAndSwap-error": func(t *testing.T) test {
cert := &acme.Certificate{
AccountID: "accounttID",
AccountID: "accountID",
OrderID: "orderID",
Leaf: leaf,
Intermediates: []*x509.Certificate{inter, root},
Expand All @@ -48,12 +48,11 @@ func TestDB_CreateCertificate(t *testing.T) {

dbc := new(dbCert)
assert.FatalError(t, json.Unmarshal(nu, dbc))
assert.FatalError(t, err)
assert.Equals(t, dbc.ID, string(key))
assert.Equals(t, dbc.ID, cert.ID)
assert.Equals(t, dbc.AccountID, cert.AccountID)
assert.True(t, clock.Now().Add(-time.Minute).Before(dbc.Created))
assert.True(t, clock.Now().Add(time.Minute).After(dbc.Created))
assert.True(t, clock.Now().Add(-time.Minute).Before(dbc.CreatedAt))
assert.True(t, clock.Now().Add(time.Minute).After(dbc.CreatedAt))
return nil, false, errors.New("force")
},
},
Expand All @@ -63,7 +62,7 @@ func TestDB_CreateCertificate(t *testing.T) {
},
"ok": func(t *testing.T) test {
cert := &acme.Certificate{
AccountID: "accounttID",
AccountID: "accountID",
OrderID: "orderID",
Leaf: leaf,
Intermediates: []*x509.Certificate{inter, root},
Expand All @@ -83,12 +82,11 @@ func TestDB_CreateCertificate(t *testing.T) {

dbc := new(dbCert)
assert.FatalError(t, json.Unmarshal(nu, dbc))
assert.FatalError(t, err)
assert.Equals(t, dbc.ID, string(key))
assert.Equals(t, dbc.ID, cert.ID)
assert.Equals(t, dbc.AccountID, cert.AccountID)
assert.True(t, clock.Now().Add(-time.Minute).Before(dbc.Created))
assert.True(t, clock.Now().Add(time.Minute).After(dbc.Created))
assert.True(t, clock.Now().Add(-time.Minute).Before(dbc.CreatedAt))
assert.True(t, clock.Now().Add(time.Minute).After(dbc.CreatedAt))
return nil, true, nil
},
},
Expand Down Expand Up @@ -124,8 +122,9 @@ func TestDB_GetCertificate(t *testing.T) {

certID := "certID"
type test struct {
db nosql.DB
err error
db nosql.DB
err error
acmeErr *acme.Error
}
var tests = map[string]func(t *testing.T) test{
"fail/not-found": func(t *testing.T) test {
Expand All @@ -138,7 +137,7 @@ func TestDB_GetCertificate(t *testing.T) {
return nil, nosqldb.ErrNotFound
},
},
err: errors.New("certificate certID not found"),
acmeErr: acme.NewError(acme.ErrorMalformedType, "certificate certID not found"),
}
},
"fail/db.Get-error": func(t *testing.T) test {
Expand Down Expand Up @@ -182,7 +181,7 @@ func TestDB_GetCertificate(t *testing.T) {
Type: "Public Key",
Bytes: leaf.Raw,
}),
Created: clock.Now(),
CreatedAt: clock.Now(),
}
b, err := json.Marshal(cert)
assert.FatalError(t, err)
Expand Down Expand Up @@ -215,7 +214,7 @@ func TestDB_GetCertificate(t *testing.T) {
Type: "CERTIFICATE",
Bytes: root.Raw,
})...),
Created: clock.Now(),
CreatedAt: clock.Now(),
}
b, err := json.Marshal(cert)
assert.FatalError(t, err)
Expand All @@ -232,8 +231,19 @@ func TestDB_GetCertificate(t *testing.T) {
db := DB{db: tc.db}
cert, err := db.GetCertificate(context.Background(), certID)
if err != nil {
if assert.NotNil(t, tc.err) {
assert.HasPrefix(t, err.Error(), tc.err.Error())
switch k := err.(type) {
case *acme.Error:
if assert.NotNil(t, tc.acmeErr) {
assert.Equals(t, k.Type, tc.acmeErr.Type)
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
assert.Equals(t, k.Status, tc.acmeErr.Status)
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
}
default:
if assert.NotNil(t, tc.err) {
assert.HasPrefix(t, err.Error(), tc.err.Error())
}
}
} else {
if assert.Nil(t, tc.err) {
Expand Down
50 changes: 24 additions & 26 deletions acme/db/nosql/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ import (
"github.com/smallstep/nosql"
)

// dbChallenge is the base Challenge type that others build from.
type dbChallenge struct {
ID string `json:"id"`
AccountID string `json:"accountID"`
AuthzID string `json:"authzID"`
Type string `json:"type"`
Status acme.Status `json:"status"`
Token string `json:"token"`
Value string `json:"value"`
Validated string `json:"validated"`
Created time.Time `json:"created"`
Error *acme.Error `json:"error"`
ID string `json:"id"`
AccountID string `json:"accountID"`
AuthzID string `json:"authzID"`
Type string `json:"type"`
Status acme.Status `json:"status"`
Token string `json:"token"`
Value string `json:"value"`
ValidatedAt string `json:"validatedAt"`
CreatedAt time.Time `json:"createdAt"`
Error *acme.Error `json:"error"`
}

func (dbc *dbChallenge) clone() *dbChallenge {
Expand All @@ -32,9 +31,9 @@ func (dbc *dbChallenge) clone() *dbChallenge {
func (db *DB) getDBChallenge(ctx context.Context, id string) (*dbChallenge, error) {
data, err := db.db.Get(challengeTable, []byte(id))
if nosql.IsErrNotFound(err) {
return nil, errors.Wrapf(err, "challenge %s not found", id)
return nil, acme.NewError(acme.ErrorMalformedType, "challenge %s not found", id)
} else if err != nil {
return nil, errors.Wrapf(err, "error loading challenge %s", id)
return nil, errors.Wrapf(err, "error loading acme challenge %s", id)
}

dbch := new(dbChallenge)
Expand All @@ -60,7 +59,7 @@ func (db *DB) CreateChallenge(ctx context.Context, ch *acme.Challenge) error {
Value: ch.Value,
Status: acme.StatusPending,
Token: ch.Token,
Created: clock.Now(),
CreatedAt: clock.Now(),
Type: ch.Type,
}

Expand All @@ -76,33 +75,32 @@ func (db *DB) GetChallenge(ctx context.Context, id, authzID string) (*acme.Chall
}

ch := &acme.Challenge{
Type: dbch.Type,
Status: dbch.Status,
Token: dbch.Token,
ID: dbch.ID,
AuthzID: dbch.AuthzID,
Error: dbch.Error,
Validated: dbch.Validated,
ID: dbch.ID,
AccountID: dbch.AccountID,
AuthzID: dbch.AuthzID,
Type: dbch.Type,
Value: dbch.Value,
Status: dbch.Status,
Token: dbch.Token,
Error: dbch.Error,
ValidatedAt: dbch.ValidatedAt,
}
return ch, nil
}

// UpdateChallenge updates an ACME challenge type in the database.
func (db *DB) UpdateChallenge(ctx context.Context, ch *acme.Challenge) error {
if len(ch.ID) == 0 {
return errors.New("id cannot be empty")
}
old, err := db.getDBChallenge(ctx, ch.ID)
if err != nil {
return err
}

nu := old.clone()

// These should be the only values chaning in an Update request.
// These should be the only values changing in an Update request.
nu.Status = ch.Status
nu.Error = ch.Error
nu.Validated = ch.Validated
nu.ValidatedAt = ch.ValidatedAt

return db.save(ctx, old.ID, nu, old, "challenge", challengeTable)
}

0 comments on commit 206909b

Please sign in to comment.