forked from TrueCloudLab/certificates
wip
This commit is contained in:
parent
af3cf7dae9
commit
98a6e54530
7 changed files with 194 additions and 126 deletions
|
@ -4,23 +4,19 @@ import "context"
|
|||
|
||||
// Admin type.
|
||||
type Admin struct {
|
||||
ID string `json:"-"`
|
||||
ID string `json:"id"`
|
||||
AuthorityID string `json:"-"`
|
||||
ProvisionerID string `json:"provisionerID"`
|
||||
Name string `json:"name"`
|
||||
ProvisionerName string `json:"provisionerName"`
|
||||
ProvisionerType string `json:"provisionerType"`
|
||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||
Status StatusType `json:"status"`
|
||||
}
|
||||
|
||||
// CreateAdmin builds and stores an admin type in the DB.
|
||||
func CreateAdmin(ctx context.Context, db DB, name string, prov *Provisioner, isSuperAdmin bool) (*Admin, error) {
|
||||
func CreateAdmin(ctx context.Context, db DB, name string, provID string, isSuperAdmin bool) (*Admin, error) {
|
||||
adm := &Admin{
|
||||
Name: name,
|
||||
ProvisionerID: prov.ID,
|
||||
ProvisionerName: prov.Name,
|
||||
ProvisionerType: prov.Type,
|
||||
ProvisionerID: provID,
|
||||
IsSuperAdmin: isSuperAdmin,
|
||||
Status: StatusActive,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
@ -15,7 +16,9 @@ type CreateProvisionerRequest struct {
|
|||
Claims *mgmt.Claims `json:"claims"`
|
||||
Details interface{} `json:"details"`
|
||||
X509Template string `json:"x509Template"`
|
||||
X509TemplateData []byte `json:"x509TemplateData"`
|
||||
SSHTemplate string `json:"sshTemplate"`
|
||||
SSHTemplateData []byte `json:"sshTemplateData"`
|
||||
}
|
||||
|
||||
// Validate validates a new-provisioner request body.
|
||||
|
@ -28,7 +31,9 @@ type UpdateProvisionerRequest struct {
|
|||
Claims *mgmt.Claims `json:"claims"`
|
||||
Details interface{} `json:"details"`
|
||||
X509Template string `json:"x509Template"`
|
||||
X509TemplateData []byte `json:"x509TemplateData"`
|
||||
SSHTemplate string `json:"sshTemplate"`
|
||||
SSHTemplateData []byte `json:"sshTemplateData"`
|
||||
}
|
||||
|
||||
// Validate validates a new-provisioner request body.
|
||||
|
@ -58,6 +63,7 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) {
|
|||
api.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("provs = %+v\n", provs)
|
||||
api.JSON(w, provs)
|
||||
}
|
||||
|
||||
|
@ -80,7 +86,9 @@ func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
|
|||
Claims: body.Claims,
|
||||
Details: body.Details,
|
||||
X509Template: body.X509Template,
|
||||
X509TemplateData: body.X509TemplateData,
|
||||
SSHTemplate: body.SSHTemplate,
|
||||
SSHTemplateData: body.SSHTemplateData,
|
||||
}
|
||||
if err := h.db.CreateProvisioner(ctx, prov); err != nil {
|
||||
api.WriteError(w, err)
|
||||
|
|
|
@ -2,6 +2,7 @@ package mgmt
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/authority/config"
|
||||
|
@ -24,6 +25,17 @@ const (
|
|||
StatusDeleted
|
||||
)
|
||||
|
||||
func (st StatusType) String() string {
|
||||
switch st {
|
||||
case StatusActive:
|
||||
return "active"
|
||||
case StatusDeleted:
|
||||
return "deleted"
|
||||
default:
|
||||
return fmt.Sprintf("status %d not found", st)
|
||||
}
|
||||
}
|
||||
|
||||
// Claims encapsulates all x509 and ssh claims applied to the authority
|
||||
// configuration. E.g. maxTLSCertDuration, defaultSSHCertDuration, etc.
|
||||
type Claims struct {
|
||||
|
@ -111,7 +123,7 @@ func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*A
|
|||
return nil, WrapErrorISE(err, "error creating first provisioner")
|
||||
}
|
||||
|
||||
admin, err := CreateAdmin(ctx, db, "Change Me", prov, true)
|
||||
admin, err := CreateAdmin(ctx, db, "Change Me", prov.ID, true)
|
||||
if err != nil {
|
||||
// TODO should we try to clean up?
|
||||
return nil, WrapErrorISE(err, "error creating first provisioner")
|
||||
|
|
|
@ -63,19 +63,13 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*mgmt.Admin, error) {
|
|||
return nil, err
|
||||
}
|
||||
if adm.Status == mgmt.StatusDeleted {
|
||||
return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted")
|
||||
return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", adm.ID)
|
||||
}
|
||||
if adm.AuthorityID != db.authorityID {
|
||||
return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType,
|
||||
"admin %s is not owned by authority %s", adm.ID, db.authorityID)
|
||||
}
|
||||
|
||||
prov, err := db.GetProvisioner(ctx, adm.ProvisionerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adm.ProvisionerName = prov.Name
|
||||
adm.ProvisionerType = prov.Type
|
||||
return adm, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package nosql
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -17,9 +18,11 @@ type dbProvisioner struct {
|
|||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Claims *mgmt.Claims `json:"claims"`
|
||||
Details interface{} `json:"details"`
|
||||
Details []byte `json:"details"`
|
||||
X509Template string `json:"x509Template"`
|
||||
X509TemplateData []byte `json:"x509TemplateData"`
|
||||
SSHTemplate string `json:"sshTemplate"`
|
||||
SSHTemplateData []byte `json:"sshTemplateData"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
DeletedAt time.Time `json:"deletedAt"`
|
||||
}
|
||||
|
@ -84,19 +87,36 @@ func unmarshalDBProvisioner(data []byte, id string) (*dbProvisioner, error) {
|
|||
return dbp, nil
|
||||
}
|
||||
|
||||
type detailsType struct {
|
||||
Type mgmt.ProvisionerType
|
||||
}
|
||||
|
||||
func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) {
|
||||
dbp, err := unmarshalDBProvisioner(data, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dt := new(detailsType)
|
||||
if err := json.Unmarshal(dbp.Details, dt); err != nil {
|
||||
return nil, mgmt.WrapErrorISE(err, "error unmarshaling details to detailsType for provisioner %s", id)
|
||||
}
|
||||
details, err := unmarshalDetails(dt.Type, dbp.Details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prov := &mgmt.Provisioner{
|
||||
ID: dbp.ID,
|
||||
AuthorityID: dbp.AuthorityID,
|
||||
Type: dbp.Type,
|
||||
Name: dbp.Name,
|
||||
Claims: dbp.Claims,
|
||||
Details: details,
|
||||
X509Template: dbp.X509Template,
|
||||
X509TemplateData: dbp.X509TemplateData,
|
||||
SSHTemplate: dbp.SSHTemplate,
|
||||
SSHTemplateData: dbp.SSHTemplateData,
|
||||
}
|
||||
if !dbp.DeletedAt.IsZero() {
|
||||
prov.Status = mgmt.StatusDeleted
|
||||
|
@ -172,3 +192,66 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err
|
|||
|
||||
return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable)
|
||||
}
|
||||
|
||||
func unmarshalDetails(typ ProvisionerType, details []byte) (interface{}, error) {
|
||||
if !s.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
var v isProvisionerDetails_Data
|
||||
switch typ {
|
||||
case ProvisionerTypeJWK:
|
||||
p := new(ProvisionerDetailsJWK)
|
||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.JWK.Key.Key == nil {
|
||||
key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.JWK.Key = key
|
||||
}
|
||||
return &ProvisionerDetails{Data: p}, nil
|
||||
case ProvisionerType_OIDC:
|
||||
v = new(ProvisionerDetails_OIDC)
|
||||
case ProvisionerType_GCP:
|
||||
v = new(ProvisionerDetails_GCP)
|
||||
case ProvisionerType_AWS:
|
||||
v = new(ProvisionerDetails_AWS)
|
||||
case ProvisionerType_AZURE:
|
||||
v = new(ProvisionerDetails_Azure)
|
||||
case ProvisionerType_ACME:
|
||||
v = new(ProvisionerDetails_ACME)
|
||||
case ProvisionerType_X5C:
|
||||
p := new(ProvisionerDetails_X5C)
|
||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, k := range p.X5C.GetRoots() {
|
||||
if err := k.Select(ctx, db, k.Id.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &ProvisionerDetails{Data: p}, nil
|
||||
case ProvisionerType_K8SSA:
|
||||
p := new(ProvisionerDetails_K8SSA)
|
||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, k := range p.K8SSA.GetPublicKeys() {
|
||||
if err := k.Select(ctx, db, k.Id.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &ProvisionerDetails{Data: p}, nil
|
||||
case ProvisionerType_SSHPOP:
|
||||
v = new(ProvisionerDetails_SSHPOP)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported provisioner type %s", typ)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(s.String), v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ProvisionerDetails{Data: v}, nil
|
||||
}
|
||||
|
|
|
@ -20,6 +20,17 @@ type ProvisionerCtx struct {
|
|||
Password string
|
||||
}
|
||||
|
||||
type ProvisionerType string
|
||||
|
||||
var (
|
||||
ProvisionerTypeJWK = ProvisionerType("JWK")
|
||||
ProvisionerTypeOIDC = ProvisionerType("OIDC")
|
||||
ProvisionerTypeACME = ProvisionerType("ACME")
|
||||
ProvisionerTypeX5C = ProvisionerType("X5C")
|
||||
ProvisionerTypeK8S = ProvisionerType("K8S")
|
||||
ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP")
|
||||
)
|
||||
|
||||
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
|
||||
pc := &ProvisionerCtx{
|
||||
Claims: NewDefaultClaims(),
|
||||
|
@ -97,12 +108,14 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro
|
|||
return p, nil
|
||||
}
|
||||
|
||||
type ProvisionerDetails_JWK struct {
|
||||
// ProvisionerDetailsJWK represents the values required by a JWK provisioner.
|
||||
type ProvisionerDetailsJWK struct {
|
||||
Type ProvisionerType `json:"type"`
|
||||
PubKey []byte `json:"pubKey"`
|
||||
PrivKey string `json:"privKey"`
|
||||
EncPrivKey string `json:"privKey"`
|
||||
}
|
||||
|
||||
func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetails_JWK, error) {
|
||||
func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) {
|
||||
var err error
|
||||
|
||||
if pc.JWK != nil && pc.JWE == nil {
|
||||
|
@ -131,9 +144,10 @@ func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetails_JWK, error) {
|
|||
return nil, WrapErrorISE(err, "error serializing JWE")
|
||||
}
|
||||
|
||||
return &ProvisionerDetails_JWK{
|
||||
return &ProvisionerDetailsJWK{
|
||||
Type: ProvisionerTypeJWK,
|
||||
PubKey: jwkPubBytes,
|
||||
PrivKey: jwePrivStr,
|
||||
EncPrivKey: jwePrivStr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -150,7 +164,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
|
|||
}
|
||||
|
||||
switch details := p.Details.(type) {
|
||||
case *ProvisionerDetails_JWK:
|
||||
case *ProvisionerDetailsJWK:
|
||||
jwk := new(jose.JSONWebKey)
|
||||
if err := json.Unmarshal(details.PubKey, &jwk); err != nil {
|
||||
return nil, err
|
||||
|
@ -159,7 +173,7 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
|
|||
Type: p.Type,
|
||||
Name: p.Name,
|
||||
Key: jwk,
|
||||
EncryptedKey: details.PrivKey,
|
||||
EncryptedKey: details.EncPrivKey,
|
||||
Claims: claims,
|
||||
Options: p.GetOptions(),
|
||||
}, nil
|
||||
|
@ -324,68 +338,6 @@ func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func unmarshalDetails(ctx context.Context, db database.DB, typ ProvisionerType, s sql.NullString) (*ProvisionerDetails, error) {
|
||||
if !s.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
var v isProvisionerDetails_Data
|
||||
switch typ {
|
||||
case ProvisionerType_JWK:
|
||||
p := new(ProvisionerDetails_JWK)
|
||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.JWK.Key.Key == nil {
|
||||
key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.JWK.Key = key
|
||||
}
|
||||
return &ProvisionerDetails{Data: p}, nil
|
||||
case ProvisionerType_OIDC:
|
||||
v = new(ProvisionerDetails_OIDC)
|
||||
case ProvisionerType_GCP:
|
||||
v = new(ProvisionerDetails_GCP)
|
||||
case ProvisionerType_AWS:
|
||||
v = new(ProvisionerDetails_AWS)
|
||||
case ProvisionerType_AZURE:
|
||||
v = new(ProvisionerDetails_Azure)
|
||||
case ProvisionerType_ACME:
|
||||
v = new(ProvisionerDetails_ACME)
|
||||
case ProvisionerType_X5C:
|
||||
p := new(ProvisionerDetails_X5C)
|
||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, k := range p.X5C.GetRoots() {
|
||||
if err := k.Select(ctx, db, k.Id.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &ProvisionerDetails{Data: p}, nil
|
||||
case ProvisionerType_K8SSA:
|
||||
p := new(ProvisionerDetails_K8SSA)
|
||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, k := range p.K8SSA.GetPublicKeys() {
|
||||
if err := k.Select(ctx, db, k.Id.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &ProvisionerDetails{Data: p}, nil
|
||||
case ProvisionerType_SSHPOP:
|
||||
v = new(ProvisionerDetails_SSHPOP)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported provisioner type %s", typ)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(s.String), v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ProvisionerDetails{Data: v}, nil
|
||||
}
|
||||
|
||||
func marshalClaims(c *Claims) (sql.NullString, error) {
|
||||
b, err := json.Marshal(c)
|
||||
|
|
|
@ -105,3 +105,26 @@ retry:
|
|||
}
|
||||
return *admins, nil
|
||||
}
|
||||
|
||||
// GetProvisioners performs the GET /mgmt/provisioners request to the CA.
|
||||
func (c *MgmtClient) GetProvisioners() ([]*mgmt.Provisioner, error) {
|
||||
var retried bool
|
||||
u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/provisioners"})
|
||||
retry:
|
||||
resp, err := c.client.Get(u.String())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "client GET %s failed", u)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readError(resp.Body)
|
||||
}
|
||||
var provs = new([]*mgmt.Provisioner)
|
||||
if err := readJSON(resp.Body, provs); err != nil {
|
||||
return nil, errors.Wrapf(err, "error reading %s", u)
|
||||
}
|
||||
return *provs, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue