6ba20209c2
This commit makes sure that the attestation certificate key matches the key used on the CSR on an ACME device attestation flow.
156 lines
4.6 KiB
Go
156 lines
4.6 KiB
Go
package nosql
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/smallstep/certificates/acme"
|
|
"github.com/smallstep/nosql"
|
|
)
|
|
|
|
// dbAuthz is the base authz type that others build from.
|
|
type dbAuthz struct {
|
|
ID string `json:"id"`
|
|
AccountID string `json:"accountID"`
|
|
Identifier acme.Identifier `json:"identifier"`
|
|
Status acme.Status `json:"status"`
|
|
Token string `json:"token"`
|
|
Fingerprint string `json:"fingerprint,omitempty"`
|
|
ChallengeIDs []string `json:"challengeIDs"`
|
|
Wildcard bool `json:"wildcard"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
ExpiresAt time.Time `json:"expiresAt"`
|
|
Error *acme.Error `json:"error"`
|
|
}
|
|
|
|
func (ba *dbAuthz) clone() *dbAuthz {
|
|
u := *ba
|
|
return &u
|
|
}
|
|
|
|
// getDBAuthz retrieves and unmarshals a database representation of the
|
|
// ACME Authorization type.
|
|
func (db *DB) getDBAuthz(ctx context.Context, id string) (*dbAuthz, error) {
|
|
data, err := db.db.Get(authzTable, []byte(id))
|
|
if nosql.IsErrNotFound(err) {
|
|
return nil, acme.NewError(acme.ErrorMalformedType, "authz %s not found", id)
|
|
} else if err != nil {
|
|
return nil, errors.Wrapf(err, "error loading authz %s", id)
|
|
}
|
|
|
|
var dbaz dbAuthz
|
|
if err = json.Unmarshal(data, &dbaz); err != nil {
|
|
return nil, errors.Wrapf(err, "error unmarshaling authz %s into dbAuthz", id)
|
|
}
|
|
return &dbaz, nil
|
|
}
|
|
|
|
// GetAuthorization retrieves and unmarshals an ACME authz type from the database.
|
|
// Implements acme.DB GetAuthorization interface.
|
|
func (db *DB) GetAuthorization(ctx context.Context, id string) (*acme.Authorization, error) {
|
|
dbaz, err := db.getDBAuthz(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var chs = make([]*acme.Challenge, len(dbaz.ChallengeIDs))
|
|
for i, chID := range dbaz.ChallengeIDs {
|
|
chs[i], err = db.GetChallenge(ctx, chID, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &acme.Authorization{
|
|
ID: dbaz.ID,
|
|
AccountID: dbaz.AccountID,
|
|
Identifier: dbaz.Identifier,
|
|
Status: dbaz.Status,
|
|
Challenges: chs,
|
|
Wildcard: dbaz.Wildcard,
|
|
ExpiresAt: dbaz.ExpiresAt,
|
|
Token: dbaz.Token,
|
|
Fingerprint: dbaz.Fingerprint,
|
|
Error: dbaz.Error,
|
|
}, nil
|
|
}
|
|
|
|
// CreateAuthorization creates an entry in the database for the Authorization.
|
|
// Implements the acme.DB.CreateAuthorization interface.
|
|
func (db *DB) CreateAuthorization(ctx context.Context, az *acme.Authorization) error {
|
|
var err error
|
|
az.ID, err = randID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
chIDs := make([]string, len(az.Challenges))
|
|
for i, ch := range az.Challenges {
|
|
chIDs[i] = ch.ID
|
|
}
|
|
|
|
now := clock.Now()
|
|
dbaz := &dbAuthz{
|
|
ID: az.ID,
|
|
AccountID: az.AccountID,
|
|
Status: az.Status,
|
|
CreatedAt: now,
|
|
ExpiresAt: az.ExpiresAt,
|
|
Identifier: az.Identifier,
|
|
ChallengeIDs: chIDs,
|
|
Token: az.Token,
|
|
Fingerprint: az.Fingerprint,
|
|
Wildcard: az.Wildcard,
|
|
}
|
|
|
|
return db.save(ctx, az.ID, dbaz, nil, "authz", authzTable)
|
|
}
|
|
|
|
// UpdateAuthorization saves an updated ACME Authorization to the database.
|
|
func (db *DB) UpdateAuthorization(ctx context.Context, az *acme.Authorization) error {
|
|
old, err := db.getDBAuthz(ctx, az.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nu := old.clone()
|
|
nu.Status = az.Status
|
|
nu.Fingerprint = az.Fingerprint
|
|
nu.Error = az.Error
|
|
return db.save(ctx, old.ID, nu, old, "authz", authzTable)
|
|
}
|
|
|
|
// GetAuthorizationsByAccountID retrieves and unmarshals ACME authz types from the database.
|
|
func (db *DB) GetAuthorizationsByAccountID(ctx context.Context, accountID string) ([]*acme.Authorization, error) {
|
|
entries, err := db.db.List(authzTable)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error listing authz")
|
|
}
|
|
authzs := []*acme.Authorization{}
|
|
for _, entry := range entries {
|
|
dbaz := new(dbAuthz)
|
|
if err = json.Unmarshal(entry.Value, dbaz); err != nil {
|
|
return nil, errors.Wrapf(err, "error unmarshaling dbAuthz key '%s' into dbAuthz struct", string(entry.Key))
|
|
}
|
|
// Filter out all dbAuthzs that don't belong to the accountID. This
|
|
// could be made more efficient with additional data structures mapping the
|
|
// Account ID to authorizations. Not trivial to do, though.
|
|
if dbaz.AccountID != accountID {
|
|
continue
|
|
}
|
|
authzs = append(authzs, &acme.Authorization{
|
|
ID: dbaz.ID,
|
|
AccountID: dbaz.AccountID,
|
|
Identifier: dbaz.Identifier,
|
|
Status: dbaz.Status,
|
|
Challenges: nil, // challenges not required for current use case
|
|
Wildcard: dbaz.Wildcard,
|
|
ExpiresAt: dbaz.ExpiresAt,
|
|
Token: dbaz.Token,
|
|
Fingerprint: dbaz.Fingerprint,
|
|
Error: dbaz.Error,
|
|
})
|
|
}
|
|
|
|
return authzs, nil
|
|
}
|