certificates/authority/admin/db/nosql/admin.go

177 lines
4.6 KiB
Go
Raw Permalink Normal View History

2021-05-03 19:48:20 +00:00
package nosql
import (
"context"
"encoding/json"
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/admin"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/nosql"
"go.step.sm/linkedca"
"google.golang.org/protobuf/types/known/timestamppb"
2021-05-03 19:48:20 +00:00
)
// dbAdmin is the database representation of the Admin type.
type dbAdmin struct {
2021-05-25 23:52:06 +00:00
ID string `json:"id"`
AuthorityID string `json:"authorityID"`
ProvisionerID string `json:"provisionerID"`
Subject string `json:"subject"`
Type linkedca.Admin_Type `json:"type"`
CreatedAt time.Time `json:"createdAt"`
DeletedAt time.Time `json:"deletedAt"`
2021-05-03 19:48:20 +00:00
}
func (dba *dbAdmin) convert() *linkedca.Admin {
return &linkedca.Admin{
Id: dba.ID,
AuthorityId: dba.AuthorityID,
ProvisionerId: dba.ProvisionerID,
Subject: dba.Subject,
Type: dba.Type,
CreatedAt: timestamppb.New(dba.CreatedAt),
DeletedAt: timestamppb.New(dba.DeletedAt),
}
}
func (dba *dbAdmin) clone() *dbAdmin {
u := *dba
2021-05-03 19:48:20 +00:00
return &u
}
2023-05-10 06:47:28 +00:00
func (db *DB) getDBAdminBytes(_ context.Context, id string) ([]byte, error) {
data, err := db.db.Get(adminsTable, []byte(id))
2021-05-03 19:48:20 +00:00
if nosql.IsErrNotFound(err) {
return nil, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id)
2021-05-03 19:48:20 +00:00
} else if err != nil {
return nil, errors.Wrapf(err, "error loading admin %s", id)
}
return data, nil
}
func (db *DB) unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) {
var dba = new(dbAdmin)
if err := json.Unmarshal(data, dba); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id)
2021-05-03 19:48:20 +00:00
}
if !dba.DeletedAt.IsZero() {
return nil, admin.NewError(admin.ErrorDeletedType, "admin %s is deleted", id)
2021-05-03 19:48:20 +00:00
}
if dba.AuthorityID != db.authorityID {
return nil, admin.NewError(admin.ErrorAuthorityMismatchType,
2021-05-03 19:48:20 +00:00
"admin %s is not owned by authority %s", dba.ID, db.authorityID)
}
return dba, nil
}
func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) {
data, err := db.getDBAdminBytes(ctx, id)
if err != nil {
return nil, err
2021-05-03 19:48:20 +00:00
}
dba, err := db.unmarshalDBAdmin(data, id)
if err != nil {
return nil, err
2021-05-25 23:52:06 +00:00
}
2021-05-03 19:48:20 +00:00
return dba, nil
}
func (db *DB) unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) {
dba, err := db.unmarshalDBAdmin(data, id)
2021-05-25 23:52:06 +00:00
if err != nil {
return nil, err
2021-05-03 19:48:20 +00:00
}
return dba.convert(), nil
2021-05-03 19:48:20 +00:00
}
2021-05-18 04:07:25 +00:00
// GetAdmin retrieves and unmarshals a admin from the database.
2021-05-25 23:52:06 +00:00
func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) {
2021-05-18 04:07:25 +00:00
data, err := db.getDBAdminBytes(ctx, id)
if err != nil {
return nil, err
}
adm, err := db.unmarshalAdmin(data, id)
2021-05-18 04:07:25 +00:00
if err != nil {
return nil, err
}
return adm, nil
}
2021-05-03 19:48:20 +00:00
// GetAdmins retrieves and unmarshals all active (not deleted) admins
// from the database.
// TODO should we be paginating?
2023-05-10 06:47:28 +00:00
func (db *DB) GetAdmins(context.Context) ([]*linkedca.Admin, error) {
dbEntries, err := db.db.List(adminsTable)
2021-05-03 19:48:20 +00:00
if err != nil {
return nil, errors.Wrap(err, "error loading admins")
}
2021-05-25 23:52:06 +00:00
var admins = []*linkedca.Admin{}
2021-05-03 19:48:20 +00:00
for _, entry := range dbEntries {
adm, err := db.unmarshalAdmin(entry.Value, string(entry.Key))
2021-05-03 19:48:20 +00:00
if err != nil {
var ae *admin.Error
if errors.As(err, &ae) {
if ae.IsType(admin.ErrorDeletedType) || ae.IsType(admin.ErrorAuthorityMismatchType) {
continue
}
return nil, err
}
2023-05-10 06:47:28 +00:00
return nil, err
2021-05-03 19:48:20 +00:00
}
2021-05-25 23:52:06 +00:00
if adm.AuthorityId != db.authorityID {
2021-05-03 19:48:20 +00:00
continue
}
admins = append(admins, adm)
}
return admins, nil
}
// CreateAdmin stores a new admin to the database.
2021-05-25 23:52:06 +00:00
func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error {
2021-05-03 19:48:20 +00:00
var err error
2021-05-25 23:52:06 +00:00
adm.Id, err = randID()
2021-05-03 19:48:20 +00:00
if err != nil {
return admin.WrapErrorISE(err, "error generating random id for admin")
2021-05-03 19:48:20 +00:00
}
2021-05-25 23:52:06 +00:00
adm.AuthorityId = db.authorityID
2021-05-03 19:48:20 +00:00
dba := &dbAdmin{
2021-05-25 23:52:06 +00:00
ID: adm.Id,
2021-05-06 06:02:42 +00:00
AuthorityID: db.authorityID,
2021-05-25 23:52:06 +00:00
ProvisionerID: adm.ProvisionerId,
2021-05-18 04:07:25 +00:00
Subject: adm.Subject,
Type: adm.Type,
2021-05-06 06:02:42 +00:00
CreatedAt: clock.Now(),
2021-05-03 19:48:20 +00:00
}
return db.save(ctx, dba.ID, dba, nil, "admin", adminsTable)
2021-05-03 19:48:20 +00:00
}
// UpdateAdmin saves an updated admin to the database.
2021-05-25 23:52:06 +00:00
func (db *DB) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error {
old, err := db.getDBAdmin(ctx, adm.Id)
2021-05-03 19:48:20 +00:00
if err != nil {
return err
}
nu := old.clone()
2021-05-25 23:52:06 +00:00
nu.Type = adm.Type
return db.save(ctx, old.ID, nu, old, "admin", adminsTable)
2021-05-25 23:52:06 +00:00
}
2021-05-03 19:48:20 +00:00
2021-05-25 23:52:06 +00:00
// DeleteAdmin saves an updated admin to the database.
func (db *DB) DeleteAdmin(ctx context.Context, id string) error {
old, err := db.getDBAdmin(ctx, id)
if err != nil {
return err
2021-05-03 19:48:20 +00:00
}
2021-05-25 23:52:06 +00:00
nu := old.clone()
nu.DeletedAt = clock.Now()
2021-05-03 19:48:20 +00:00
return db.save(ctx, old.ID, nu, old, "admin", adminsTable)
2021-05-03 19:48:20 +00:00
}