smallstep-certificates/acme/db/nosql/nosql.go

104 lines
3.0 KiB
Go
Raw Normal View History

2021-02-25 18:24:24 +00:00
package nosql
import (
2021-02-28 01:05:37 +00:00
"context"
"encoding/json"
2021-03-01 06:49:20 +00:00
"time"
2021-02-28 01:05:37 +00:00
"github.com/pkg/errors"
2021-02-25 18:24:24 +00:00
nosqlDB "github.com/smallstep/nosql"
2021-03-01 06:49:20 +00:00
"go.step.sm/crypto/randutil"
2021-02-25 18:24:24 +00:00
)
2021-02-28 01:05:37 +00:00
var (
accountTable = []byte("acme_accounts")
accountByKeyIDTable = []byte("acme_keyID_accountID_index")
authzTable = []byte("acme_authzs")
challengeTable = []byte("acme_challenges")
nonceTable = []byte("nonces")
orderTable = []byte("acme_orders")
ordersByAccountIDTable = []byte("acme_account_orders_index")
certTable = []byte("acme_certs")
certBySerialTable = []byte("acme_serial_certs_index")
externalAccountKeyTable = []byte("acme_external_account_keys")
externalAccountKeyIDsByReferenceTable = []byte("acme_external_account_keyID_reference_index")
externalAccountKeyIDsByProvisionerIDTable = []byte("acme_external_account_keyID_provisionerID_index")
2021-02-28 01:05:37 +00:00
)
2021-02-25 18:24:24 +00:00
// DB is a struct that implements the AcmeDB interface.
type DB struct {
db nosqlDB.DB
}
2021-02-28 01:05:37 +00:00
2021-03-01 06:49:20 +00:00
// New configures and returns a new ACME DB backend implemented using a nosql DB.
func New(db nosqlDB.DB) (*DB, error) {
tables := [][]byte{accountTable, accountByKeyIDTable, authzTable,
2021-12-09 12:58:40 +00:00
challengeTable, nonceTable, orderTable, ordersByAccountIDTable,
certTable, certBySerialTable, externalAccountKeyTable,
externalAccountKeyIDsByReferenceTable, externalAccountKeyIDsByProvisionerIDTable,
2021-12-09 12:58:40 +00:00
}
2021-03-01 06:49:20 +00:00
for _, b := range tables {
if err := db.CreateTable(b); err != nil {
return nil, errors.Wrapf(err, "error creating table %s",
string(b))
}
}
return &DB{db}, nil
}
2021-02-28 01:05:37 +00:00
// save writes the new data to the database, overwriting the old data if it
// existed.
func (db *DB) save(ctx context.Context, id string, nu, old interface{}, typ string, table []byte) error {
2021-03-24 05:12:25 +00:00
var (
err error
newB []byte
)
if nu == nil {
newB = nil
} else {
newB, err = json.Marshal(nu)
if err != nil {
return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, nu)
}
2021-02-28 01:05:37 +00:00
}
var oldB []byte
if old == nil {
oldB = nil
} else {
oldB, err = json.Marshal(old)
if err != nil {
2021-03-06 21:06:43 +00:00
return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, old)
2021-02-28 01:05:37 +00:00
}
}
2021-03-01 06:49:20 +00:00
_, swapped, err := db.db.CmpAndSwap(table, []byte(id), oldB, newB)
2021-02-28 01:05:37 +00:00
switch {
case err != nil:
2021-03-01 06:49:20 +00:00
return errors.Wrapf(err, "error saving acme %s", typ)
2021-02-28 01:05:37 +00:00
case !swapped:
2021-03-06 21:06:43 +00:00
return errors.Errorf("error saving acme %s; changed since last read", typ)
2021-02-28 01:05:37 +00:00
default:
return nil
}
}
2021-03-01 06:49:20 +00:00
var idLen = 32
func randID() (val string, err error) {
val, err = randutil.Alphanumeric(idLen)
if err != nil {
return "", errors.Wrap(err, "error generating random alphanumeric ID")
}
return val, nil
}
// Clock that returns time in UTC rounded to seconds.
type Clock struct{}
2021-03-01 06:49:20 +00:00
// Now returns the UTC time rounded to seconds.
func (c *Clock) Now() time.Time {
return time.Now().UTC().Truncate(time.Second)
2021-03-01 06:49:20 +00:00
}
var clock = new(Clock)