certificates/authority/mgmt/db/nosql/provisioner.go

220 lines
6.5 KiB
Go
Raw 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/mgmt"
2021-05-25 23:52:06 +00:00
"github.com/smallstep/certificates/linkedca"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/nosql"
)
// dbProvisioner is the database representation of a Provisioner type.
type dbProvisioner struct {
2021-05-25 23:52:06 +00:00
ID string `json:"id"`
AuthorityID string `json:"authorityID"`
Type linkedca.Provisioner_Type `json:"type"`
2021-05-18 04:07:25 +00:00
// Name is the key
2021-05-25 23:52:06 +00:00
Name string `json:"name"`
Claims *linkedca.Claims `json:"claims"`
Details []byte `json:"details"`
X509Template []byte `json:"x509Template"`
X509TemplateData []byte `json:"x509TemplateData"`
SSHTemplate []byte `json:"sshTemplate"`
SSHTemplateData []byte `json:"sshTemplateData"`
CreatedAt time.Time `json:"createdAt"`
DeletedAt time.Time `json:"deletedAt"`
2021-05-03 19:48:20 +00:00
}
2021-05-18 04:07:25 +00:00
type provisionerNameID struct {
Name string `json:"name"`
ID string `json:"id"`
}
2021-05-03 19:48:20 +00:00
func (dbp *dbProvisioner) clone() *dbProvisioner {
u := *dbp
return &u
}
func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) {
data, err := db.db.Get(authorityProvisionersTable, []byte(id))
if nosql.IsErrNotFound(err) {
return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", id)
} else if err != nil {
return nil, errors.Wrapf(err, "error loading provisioner %s", id)
}
return data, nil
}
func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, error) {
data, err := db.getDBProvisionerBytes(ctx, id)
if err != nil {
return nil, err
}
dbp, err := unmarshalDBProvisioner(data, id)
if err != nil {
return nil, err
}
2021-05-18 04:07:25 +00:00
if !dbp.DeletedAt.IsZero() {
return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", id)
}
2021-05-03 19:48:20 +00:00
if dbp.AuthorityID != db.authorityID {
return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType,
"provisioner %s is not owned by authority %s", dbp.ID, db.authorityID)
}
return dbp, nil
}
// GetProvisioner retrieves and unmarshals a provisioner from the database.
2021-05-25 23:52:06 +00:00
func (db *DB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) {
2021-05-03 19:48:20 +00:00
data, err := db.getDBProvisionerBytes(ctx, id)
if err != nil {
return nil, err
}
prov, err := unmarshalProvisioner(data, id)
if err != nil {
return nil, err
}
2021-05-25 23:52:06 +00:00
if prov.AuthorityId != db.authorityID {
2021-05-03 19:48:20 +00:00
return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType,
2021-05-25 23:52:06 +00:00
"provisioner %s is not owned by authority %s", prov.Id, db.authorityID)
2021-05-03 19:48:20 +00:00
}
return prov, nil
}
2021-05-18 04:07:25 +00:00
func unmarshalDBProvisioner(data []byte, name string) (*dbProvisioner, error) {
2021-05-03 19:48:20 +00:00
var dbp = new(dbProvisioner)
if err := json.Unmarshal(data, dbp); err != nil {
2021-05-18 04:07:25 +00:00
return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", name)
2021-05-03 19:48:20 +00:00
}
2021-05-25 23:52:06 +00:00
if !dbp.DeletedAt.IsZero() {
return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", name)
}
2021-05-03 19:48:20 +00:00
return dbp, nil
}
2021-05-25 23:52:06 +00:00
func unmarshalProvisioner(data []byte, name string) (*linkedca.Provisioner, error) {
2021-05-18 04:07:25 +00:00
dbp, err := unmarshalDBProvisioner(data, name)
2021-05-03 19:48:20 +00:00
if err != nil {
return nil, err
}
2021-05-25 23:52:06 +00:00
details, err := linkedca.UnmarshalProvisionerDetails(dbp.Type, dbp.Details)
2021-05-11 22:25:37 +00:00
if err != nil {
return nil, err
}
2021-05-25 23:52:06 +00:00
prov := &linkedca.Provisioner{
Id: dbp.ID,
AuthorityId: dbp.AuthorityID,
2021-05-11 22:25:37 +00:00
Type: dbp.Type,
Name: dbp.Name,
Claims: dbp.Claims,
Details: details,
X509Template: dbp.X509Template,
X509TemplateData: dbp.X509TemplateData,
2021-05-25 23:52:06 +00:00
SshTemplate: dbp.SSHTemplate,
SshTemplateData: dbp.SSHTemplateData,
2021-05-03 19:48:20 +00:00
}
return prov, nil
}
// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners
// from the database.
2021-05-25 23:52:06 +00:00
func (db *DB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) {
2021-05-03 19:48:20 +00:00
dbEntries, err := db.db.List(authorityProvisionersTable)
if err != nil {
2021-05-18 04:07:25 +00:00
return nil, mgmt.WrapErrorISE(err, "error loading provisioners")
2021-05-03 19:48:20 +00:00
}
2021-05-25 23:52:06 +00:00
var provs []*linkedca.Provisioner
2021-05-03 19:48:20 +00:00
for _, entry := range dbEntries {
prov, err := unmarshalProvisioner(entry.Value, string(entry.Key))
if err != nil {
return nil, err
}
2021-05-25 23:52:06 +00:00
if prov.AuthorityId != db.authorityID {
2021-05-03 19:48:20 +00:00
continue
}
provs = append(provs, prov)
}
return provs, nil
}
// CreateProvisioner stores a new provisioner to the database.
2021-05-25 23:52:06 +00:00
func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error {
2021-05-03 19:48:20 +00:00
var err error
2021-05-25 23:52:06 +00:00
prov.Id, err = randID()
2021-05-03 19:48:20 +00:00
if err != nil {
return errors.Wrap(err, "error generating random id for provisioner")
}
2021-05-26 21:55:31 +00:00
details, err := json.Marshal(prov.Details.GetData())
2021-05-12 07:03:40 +00:00
if err != nil {
2021-05-18 04:07:25 +00:00
return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name)
2021-05-12 07:03:40 +00:00
}
2021-05-03 19:48:20 +00:00
dbp := &dbProvisioner{
2021-05-25 23:52:06 +00:00
ID: prov.Id,
2021-05-18 04:07:25 +00:00
AuthorityID: db.authorityID,
Type: prov.Type,
Name: prov.Name,
Claims: prov.Claims,
Details: details,
X509Template: prov.X509Template,
X509TemplateData: prov.X509TemplateData,
2021-05-25 23:52:06 +00:00
SSHTemplate: prov.SshTemplate,
SSHTemplateData: prov.SshTemplateData,
2021-05-18 04:07:25 +00:00
CreatedAt: clock.Now(),
}
2021-05-25 23:52:06 +00:00
if err := db.save(ctx, prov.Id, dbp, nil, "provisioner", authorityProvisionersTable); err != nil {
2021-05-18 04:07:25 +00:00
return mgmt.WrapErrorISE(err, "error creating provisioner %s", prov.Name)
}
return nil
2021-05-03 19:48:20 +00:00
}
// UpdateProvisioner saves an updated provisioner to the database.
2021-05-25 23:52:06 +00:00
func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error {
old, err := db.getDBProvisioner(ctx, prov.Id)
2021-05-18 04:07:25 +00:00
if err != nil {
return err
}
2021-05-03 19:48:20 +00:00
nu := old.clone()
2021-05-18 04:07:25 +00:00
nu.Type = prov.Type
nu.Name = prov.Name
2021-05-03 19:48:20 +00:00
nu.Claims = prov.Claims
2021-05-12 07:03:40 +00:00
nu.Details, err = json.Marshal(prov.Details)
if err != nil {
2021-05-18 23:50:54 +00:00
return mgmt.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name)
2021-05-12 07:03:40 +00:00
}
2021-05-18 04:07:25 +00:00
nu.X509Template = prov.X509Template
nu.X509TemplateData = prov.X509TemplateData
2021-05-25 23:52:06 +00:00
nu.SSHTemplate = prov.SshTemplate
nu.SSHTemplateData = prov.SshTemplateData
2021-05-12 07:03:40 +00:00
2021-05-25 23:52:06 +00:00
if err := db.save(ctx, prov.Id, nu, old, "provisioner", authorityProvisionersTable); err != nil {
2021-05-18 04:07:25 +00:00
return mgmt.WrapErrorISE(err, "error updating provisioner %s", prov.Name)
2021-05-11 22:25:37 +00:00
}
2021-05-18 23:50:54 +00:00
2021-05-18 04:07:25 +00:00
return nil
2021-05-11 22:25:37 +00:00
}
2021-05-26 04:13:01 +00:00
// DeleteProvisioner saves an updated admin to the database.
func (db *DB) DeleteProvisioner(ctx context.Context, id string) error {
old, err := db.getDBProvisioner(ctx, id)
if err != nil {
return err
}
nu := old.clone()
nu.DeletedAt = clock.Now()
return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable)
}