forked from TrueCloudLab/certificates
wip admin CRUD
This commit is contained in:
parent
98a6e54530
commit
4d48072746
9 changed files with 293 additions and 154 deletions
|
@ -162,6 +162,7 @@ func (a *Authority) init() error {
|
||||||
return mgmt.WrapErrorISE(err, "error getting authConfig from db")
|
return mgmt.WrapErrorISE(err, "error getting authConfig from db")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates()
|
a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -5,13 +5,14 @@ import (
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
|
"github.com/smallstep/certificates/authority/mgmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateAdminRequest represents the body for a CreateAdmin request.
|
// CreateAdminRequest represents the body for a CreateAdmin request.
|
||||||
type CreateAdminRequest struct {
|
type CreateAdminRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Provisioner string `json:"provisioner"`
|
ProvisionerID string `json:"provisionerID"`
|
||||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a new-admin request body.
|
// Validate validates a new-admin request body.
|
||||||
|
@ -21,9 +22,10 @@ func (car *CreateAdminRequest) Validate() error {
|
||||||
|
|
||||||
// UpdateAdminRequest represents the body for a UpdateAdmin request.
|
// UpdateAdminRequest represents the body for a UpdateAdmin request.
|
||||||
type UpdateAdminRequest struct {
|
type UpdateAdminRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Provisioner string `json:"provisioner"`
|
ProvisionerID string `json:"provisionerID"`
|
||||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
IsSuperAdmin string `json:"isSuperAdmin"`
|
||||||
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a new-admin request body.
|
// Validate validates a new-admin request body.
|
||||||
|
@ -31,6 +33,11 @@ func (uar *UpdateAdminRequest) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteResponse is the resource for successful DELETE responses.
|
||||||
|
type DeleteResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
// GetAdmin returns the requested admin, or an error.
|
// GetAdmin returns the requested admin, or an error.
|
||||||
func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
@ -58,59 +65,84 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// CreateAdmin creates a new admin.
|
// CreateAdmin creates a new admin.
|
||||||
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
/*
|
ctx := r.Context()
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var body CreateAdminRequest
|
var body CreateAdminRequest
|
||||||
if err := ReadJSON(r.Body, &body); err != nil {
|
if err := api.ReadJSON(r.Body, &body); err != nil {
|
||||||
api.WriteError(w, err)
|
api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := body.Validate(); err != nil {
|
|
||||||
api.WriteError(w, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
adm := &config.Admin{
|
// TODO validate
|
||||||
Name: body.Name,
|
|
||||||
Provisioner: body.Provisioner,
|
adm := &mgmt.Admin{
|
||||||
IsSuperAdmin: body.IsSuperAdmin,
|
ProvisionerID: body.ProvisionerID,
|
||||||
}
|
Name: body.Name,
|
||||||
if err := h.db.CreateAdmin(ctx, adm); err != nil {
|
IsSuperAdmin: body.IsSuperAdmin,
|
||||||
api.WriteError(w, err)
|
Status: mgmt.StatusActive,
|
||||||
return
|
}
|
||||||
}
|
if err := h.db.CreateAdmin(ctx, adm); err != nil {
|
||||||
api.JSONStatus(w, adm, http.StatusCreated)
|
api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin"))
|
||||||
*/
|
return
|
||||||
|
}
|
||||||
|
api.JSON(w, adm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAdmin deletes admin.
|
||||||
|
func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
id := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
adm, err := h.db.GetAdmin(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
adm.Status = mgmt.StatusDeleted
|
||||||
|
if err := h.db.UpdateAdmin(ctx, adm); err != nil {
|
||||||
|
api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
api.JSON(w, &DeleteResponse{Status: "ok"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAdmin updates an existing admin.
|
// UpdateAdmin updates an existing admin.
|
||||||
func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||||
/*
|
ctx := r.Context()
|
||||||
ctx := r.Context()
|
|
||||||
id := chi.URLParam(r, "id")
|
|
||||||
|
|
||||||
var body UpdateAdminRequest
|
var body UpdateAdminRequest
|
||||||
if err := ReadJSON(r.Body, &body); err != nil {
|
if err := api.ReadJSON(r.Body, &body); err != nil {
|
||||||
api.WriteError(w, err)
|
api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := body.Validate(); err != nil {
|
|
||||||
api.WriteError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if adm, err := h.db.GetAdmin(ctx, id); err != nil {
|
|
||||||
api.WriteError(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
id := chi.URLParam(r, "id")
|
||||||
|
|
||||||
|
adm, err := h.db.GetAdmin(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
api.WriteError(w, mgmt.WrapErrorISE(err, "error retrieiving admin %s", id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO validate
|
||||||
|
|
||||||
|
if len(body.Name) > 0 {
|
||||||
adm.Name = body.Name
|
adm.Name = body.Name
|
||||||
adm.Provisioner = body.Provisioner
|
}
|
||||||
adm.IsSuperAdmin = body.IsSuperAdmin
|
if len(body.Status) > 0 {
|
||||||
|
adm.Status = mgmt.StatusActive // FIXME
|
||||||
if err := h.db.UpdateAdmin(ctx, adm); err != nil {
|
}
|
||||||
api.WriteError(w, err)
|
// Set IsSuperAdmin iff the string was set in the update request.
|
||||||
return
|
if len(body.IsSuperAdmin) > 0 {
|
||||||
}
|
adm.IsSuperAdmin = (body.IsSuperAdmin == "true")
|
||||||
api.JSON(w, adm)
|
}
|
||||||
*/
|
if len(body.ProvisionerID) > 0 {
|
||||||
|
adm.ProvisionerID = body.ProvisionerID
|
||||||
|
}
|
||||||
|
if err := h.db.UpdateAdmin(ctx, adm); err != nil {
|
||||||
|
api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
api.JSON(w, adm)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,15 @@ func (h *Handler) Route(r api.Router) {
|
||||||
r.MethodFunc("GET", "/provisioner/{id}", h.GetProvisioner)
|
r.MethodFunc("GET", "/provisioner/{id}", h.GetProvisioner)
|
||||||
r.MethodFunc("GET", "/provisioners", h.GetProvisioners)
|
r.MethodFunc("GET", "/provisioners", h.GetProvisioners)
|
||||||
r.MethodFunc("POST", "/provisioner", h.CreateProvisioner)
|
r.MethodFunc("POST", "/provisioner", h.CreateProvisioner)
|
||||||
r.MethodFunc("PUT", "/provsiioner/{id}", h.UpdateProvisioner)
|
r.MethodFunc("PUT", "/provisioner/{id}", h.UpdateProvisioner)
|
||||||
|
//r.MethodFunc("DELETE", "/provisioner/{id}", h.UpdateAdmin)
|
||||||
|
|
||||||
// Admins
|
// Admins
|
||||||
r.MethodFunc("GET", "/admin/{id}", h.GetAdmin)
|
r.MethodFunc("GET", "/admin/{id}", h.GetAdmin)
|
||||||
r.MethodFunc("GET", "/admins", h.GetAdmins)
|
r.MethodFunc("GET", "/admins", h.GetAdmins)
|
||||||
r.MethodFunc("POST", "/admin", h.CreateAdmin)
|
r.MethodFunc("POST", "/admin", h.CreateAdmin)
|
||||||
r.MethodFunc("PUT", "/admin/{id}", h.UpdateAdmin)
|
r.MethodFunc("PUT", "/admin/{id}", h.UpdateAdmin)
|
||||||
|
r.MethodFunc("DELETE", "/admin/{id}", h.DeleteAdmin)
|
||||||
|
|
||||||
// AuthConfig
|
// AuthConfig
|
||||||
r.MethodFunc("GET", "/authconfig/{id}", h.GetAuthConfig)
|
r.MethodFunc("GET", "/authconfig/{id}", h.GetAuthConfig)
|
||||||
|
|
|
@ -131,6 +131,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *mgmt.Admin) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error generating random id for admin")
|
return errors.Wrap(err, "error generating random id for admin")
|
||||||
}
|
}
|
||||||
|
adm.AuthorityID = db.authorityID
|
||||||
|
|
||||||
dba := &dbAdmin{
|
dba := &dbAdmin{
|
||||||
ID: adm.ID,
|
ID: adm.ID,
|
||||||
|
|
|
@ -126,7 +126,6 @@ func unmarshalProvisioner(data []byte, id string) (*mgmt.Provisioner, error) {
|
||||||
|
|
||||||
// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners
|
// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners
|
||||||
// from the database.
|
// from the database.
|
||||||
// TODO should we be paginating?
|
|
||||||
func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) {
|
func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) {
|
||||||
dbEntries, err := db.db.List(authorityProvisionersTable)
|
dbEntries, err := db.db.List(authorityProvisionersTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -157,13 +156,18 @@ func (db *DB) CreateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err
|
||||||
return errors.Wrap(err, "error generating random id for provisioner")
|
return errors.Wrap(err, "error generating random id for provisioner")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details, err := json.Marshal(prov.Details)
|
||||||
|
if err != nil {
|
||||||
|
return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner")
|
||||||
|
}
|
||||||
|
|
||||||
dbp := &dbProvisioner{
|
dbp := &dbProvisioner{
|
||||||
ID: prov.ID,
|
ID: prov.ID,
|
||||||
AuthorityID: db.authorityID,
|
AuthorityID: db.authorityID,
|
||||||
Type: prov.Type,
|
Type: prov.Type,
|
||||||
Name: prov.Name,
|
Name: prov.Name,
|
||||||
Claims: prov.Claims,
|
Claims: prov.Claims,
|
||||||
Details: prov.Details,
|
Details: details,
|
||||||
X509Template: prov.X509Template,
|
X509Template: prov.X509Template,
|
||||||
SSHTemplate: prov.SSHTemplate,
|
SSHTemplate: prov.SSHTemplate,
|
||||||
CreatedAt: clock.Now(),
|
CreatedAt: clock.Now(),
|
||||||
|
@ -186,72 +190,44 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err
|
||||||
nu.DeletedAt = clock.Now()
|
nu.DeletedAt = clock.Now()
|
||||||
}
|
}
|
||||||
nu.Claims = prov.Claims
|
nu.Claims = prov.Claims
|
||||||
nu.Details = prov.Details
|
|
||||||
nu.X509Template = prov.X509Template
|
nu.X509Template = prov.X509Template
|
||||||
nu.SSHTemplate = prov.SSHTemplate
|
nu.SSHTemplate = prov.SSHTemplate
|
||||||
|
|
||||||
|
nu.Details, err = json.Marshal(prov.Details)
|
||||||
|
if err != nil {
|
||||||
|
return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner")
|
||||||
|
}
|
||||||
|
|
||||||
return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable)
|
return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalDetails(typ ProvisionerType, details []byte) (interface{}, error) {
|
func unmarshalDetails(typ mgmt.ProvisionerType, data []byte) (mgmt.ProvisionerDetails, error) {
|
||||||
if !s.Valid {
|
var v mgmt.ProvisionerDetails
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
var v isProvisionerDetails_Data
|
|
||||||
switch typ {
|
switch typ {
|
||||||
case ProvisionerTypeJWK:
|
case mgmt.ProvisionerTypeJWK:
|
||||||
p := new(ProvisionerDetailsJWK)
|
v = new(mgmt.ProvisionerDetailsJWK)
|
||||||
if err := json.Unmarshal([]byte(s.String), p); err != nil {
|
case mgmt.ProvisionerTypeOIDC:
|
||||||
return nil, err
|
v = new(mgmt.ProvisionerDetailsOIDC)
|
||||||
}
|
case mgmt.ProvisionerTypeGCP:
|
||||||
if p.JWK.Key.Key == nil {
|
v = new(mgmt.ProvisionerDetailsGCP)
|
||||||
key, err := LoadKey(ctx, db, p.JWK.Key.Id.Id)
|
case mgmt.ProvisionerTypeAWS:
|
||||||
if err != nil {
|
v = new(mgmt.ProvisionerDetailsAWS)
|
||||||
return nil, err
|
case mgmt.ProvisionerTypeAZURE:
|
||||||
}
|
v = new(mgmt.ProvisionerDetailsAzure)
|
||||||
p.JWK.Key = key
|
case mgmt.ProvisionerTypeACME:
|
||||||
}
|
v = new(mgmt.ProvisionerDetailsACME)
|
||||||
return &ProvisionerDetails{Data: p}, nil
|
case mgmt.ProvisionerTypeX5C:
|
||||||
case ProvisionerType_OIDC:
|
v = new(mgmt.ProvisionerDetailsX5C)
|
||||||
v = new(ProvisionerDetails_OIDC)
|
case mgmt.ProvisionerTypeK8SSA:
|
||||||
case ProvisionerType_GCP:
|
v = new(mgmt.ProvisionerDetailsK8SSA)
|
||||||
v = new(ProvisionerDetails_GCP)
|
case mgmt.ProvisionerTypeSSHPOP:
|
||||||
case ProvisionerType_AWS:
|
v = new(mgmt.ProvisionerDetailsSSHPOP)
|
||||||
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:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported provisioner type %s", typ)
|
return nil, fmt.Errorf("unsupported provisioner type %s", typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(s.String), v); err != nil {
|
if err := json.Unmarshal(data, v); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &ProvisionerDetails{Data: v}, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ const (
|
||||||
ErrorAuthorityMismatchType
|
ErrorAuthorityMismatchType
|
||||||
// ErrorDeletedType resource has been deleted.
|
// ErrorDeletedType resource has been deleted.
|
||||||
ErrorDeletedType
|
ErrorDeletedType
|
||||||
|
// ErrorBadRequestType bad request.
|
||||||
|
ErrorBadRequestType
|
||||||
// ErrorServerInternalType internal server error.
|
// ErrorServerInternalType internal server error.
|
||||||
ErrorServerInternalType
|
ErrorServerInternalType
|
||||||
)
|
)
|
||||||
|
@ -37,6 +39,8 @@ func (ap ProblemType) String() string {
|
||||||
return "authorityMismatch"
|
return "authorityMismatch"
|
||||||
case ErrorDeletedType:
|
case ErrorDeletedType:
|
||||||
return "deleted"
|
return "deleted"
|
||||||
|
case ErrorBadRequestType:
|
||||||
|
return "badRequest"
|
||||||
case ErrorServerInternalType:
|
case ErrorServerInternalType:
|
||||||
return "internalServerError"
|
return "internalServerError"
|
||||||
default:
|
default:
|
||||||
|
@ -69,10 +73,15 @@ var (
|
||||||
status: 401,
|
status: 401,
|
||||||
},
|
},
|
||||||
ErrorDeletedType: {
|
ErrorDeletedType: {
|
||||||
typ: ErrorNotFoundType.String(),
|
typ: ErrorDeletedType.String(),
|
||||||
details: "resource is deleted",
|
details: "resource is deleted",
|
||||||
status: 403,
|
status: 403,
|
||||||
},
|
},
|
||||||
|
ErrorBadRequestType: {
|
||||||
|
typ: ErrorBadRequestType.String(),
|
||||||
|
details: "bad request",
|
||||||
|
status: 400,
|
||||||
|
},
|
||||||
ErrorServerInternalType: errorServerInternalMetadata,
|
ErrorServerInternalType: errorServerInternalMetadata,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,12 +23,15 @@ type ProvisionerCtx struct {
|
||||||
type ProvisionerType string
|
type ProvisionerType string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ProvisionerTypeJWK = ProvisionerType("JWK")
|
|
||||||
ProvisionerTypeOIDC = ProvisionerType("OIDC")
|
|
||||||
ProvisionerTypeACME = ProvisionerType("ACME")
|
ProvisionerTypeACME = ProvisionerType("ACME")
|
||||||
ProvisionerTypeX5C = ProvisionerType("X5C")
|
ProvisionerTypeAWS = ProvisionerType("AWS")
|
||||||
ProvisionerTypeK8S = ProvisionerType("K8S")
|
ProvisionerTypeAZURE = ProvisionerType("AZURE")
|
||||||
|
ProvisionerTypeGCP = ProvisionerType("GCP")
|
||||||
|
ProvisionerTypeJWK = ProvisionerType("JWK")
|
||||||
|
ProvisionerTypeK8SSA = ProvisionerType("K8SSA")
|
||||||
|
ProvisionerTypeOIDC = ProvisionerType("OIDC")
|
||||||
ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP")
|
ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP")
|
||||||
|
ProvisionerTypeX5C = ProvisionerType("X5C")
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
|
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
|
||||||
|
@ -56,8 +59,8 @@ func WithPassword(pass string) func(*ProvisionerCtx) {
|
||||||
|
|
||||||
// Provisioner type.
|
// Provisioner type.
|
||||||
type Provisioner struct {
|
type Provisioner struct {
|
||||||
ID string `json:"-"`
|
ID string `json:"id"`
|
||||||
AuthorityID string `json:"-"`
|
AuthorityID string `json:"authorityID"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Claims *Claims `json:"claims"`
|
Claims *Claims `json:"claims"`
|
||||||
|
@ -108,6 +111,10 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProvisionerDetails interface {
|
||||||
|
isProvisionerDetails()
|
||||||
|
}
|
||||||
|
|
||||||
// ProvisionerDetailsJWK represents the values required by a JWK provisioner.
|
// ProvisionerDetailsJWK represents the values required by a JWK provisioner.
|
||||||
type ProvisionerDetailsJWK struct {
|
type ProvisionerDetailsJWK struct {
|
||||||
Type ProvisionerType `json:"type"`
|
Type ProvisionerType `json:"type"`
|
||||||
|
@ -115,6 +122,64 @@ type ProvisionerDetailsJWK struct {
|
||||||
EncPrivKey string `json:"privKey"`
|
EncPrivKey string `json:"privKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsOIDC represents the values required by a OIDC provisioner.
|
||||||
|
type ProvisionerDetailsOIDC struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsGCP represents the values required by a GCP provisioner.
|
||||||
|
type ProvisionerDetailsGCP struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsAWS represents the values required by a AWS provisioner.
|
||||||
|
type ProvisionerDetailsAWS struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsAzure represents the values required by a Azure provisioner.
|
||||||
|
type ProvisionerDetailsAzure struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsACME represents the values required by a ACME provisioner.
|
||||||
|
type ProvisionerDetailsACME struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsX5C represents the values required by a X5C provisioner.
|
||||||
|
type ProvisionerDetailsX5C struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsK8SSA represents the values required by a K8SSA provisioner.
|
||||||
|
type ProvisionerDetailsK8SSA struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisionerDetailsSSHPOP represents the values required by a SSHPOP provisioner.
|
||||||
|
type ProvisionerDetailsSSHPOP struct {
|
||||||
|
Type ProvisionerType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsJWK) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsOIDC) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsGCP) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsAWS) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsAzure) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsACME) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsX5C) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsK8SSA) isProvisionerDetails() {}
|
||||||
|
|
||||||
|
func (*ProvisionerDetailsSSHPOP) isProvisionerDetails() {}
|
||||||
|
|
||||||
func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) {
|
func createJWKDetails(pc *ProvisionerCtx) (*ProvisionerDetailsJWK, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -159,10 +224,6 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch details := p.Details.(type) {
|
switch details := p.Details.(type) {
|
||||||
case *ProvisionerDetailsJWK:
|
case *ProvisionerDetailsJWK:
|
||||||
jwk := new(jose.JSONWebKey)
|
jwk := new(jose.JSONWebKey)
|
||||||
|
@ -325,36 +386,3 @@ func (c *Claims) ToCertificates() (*provisioner.Claims, error) {
|
||||||
EnableSSHCA: &c.SSH.Enabled,
|
EnableSSHCA: &c.SSH.Enabled,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func marshalDetails(d *ProvisionerDetails) (sql.NullString, error) {
|
|
||||||
b, err := json.Marshal(d.GetData())
|
|
||||||
if err != nil {
|
|
||||||
return sql.NullString{}, nil
|
|
||||||
}
|
|
||||||
return sql.NullString{
|
|
||||||
String: string(b),
|
|
||||||
Valid: len(b) > 0,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func marshalClaims(c *Claims) (sql.NullString, error) {
|
|
||||||
b, err := json.Marshal(c)
|
|
||||||
if err != nil {
|
|
||||||
return sql.NullString{}, nil
|
|
||||||
}
|
|
||||||
return sql.NullString{
|
|
||||||
String: string(b),
|
|
||||||
Valid: len(b) > 0,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalClaims(s sql.NullString) (*Claims, error) {
|
|
||||||
if !s.Valid {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
v := new(Claims)
|
|
||||||
return v, json.Unmarshal([]byte(s.String), v)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -88,6 +88,11 @@ func (c *uaClient) Post(url, contentType string, body io.Reader) (*http.Response
|
||||||
return c.Client.Do(req)
|
return c.Client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *uaClient) Do(req *http.Request) (*http.Response, error) {
|
||||||
|
req.Header.Set("User-Agent", UserAgent)
|
||||||
|
return c.Client.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
// RetryFunc defines the method used to retry a request. If it returns true, the
|
// RetryFunc defines the method used to retry a request. If it returns true, the
|
||||||
// request will be retried once.
|
// request will be retried once.
|
||||||
type RetryFunc func(code int) bool
|
type RetryFunc func(code int) bool
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package ca
|
package ca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/mgmt"
|
"github.com/smallstep/certificates/authority/mgmt"
|
||||||
|
mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api"
|
||||||
|
"github.com/smallstep/certificates/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MgmtClient implements an HTTP client for the CA server.
|
// MgmtClient implements an HTTP client for the CA server.
|
||||||
|
@ -83,6 +87,87 @@ retry:
|
||||||
return adm, nil
|
return adm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateAdmin performs the POST /mgmt/admin request to the CA.
|
||||||
|
func (c *MgmtClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*mgmt.Admin, error) {
|
||||||
|
var retried bool
|
||||||
|
body, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request")
|
||||||
|
}
|
||||||
|
u := c.endpoint.ResolveReference(&url.URL{Path: "/mgmt/admin"})
|
||||||
|
retry:
|
||||||
|
resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "client POST %s failed", u)
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
if !retried && c.retryOnError(resp) {
|
||||||
|
retried = true
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
return nil, readError(resp.Body)
|
||||||
|
}
|
||||||
|
var adm = new(mgmt.Admin)
|
||||||
|
if err := readJSON(resp.Body, adm); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error reading %s", u)
|
||||||
|
}
|
||||||
|
return adm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAdmin performs the DELETE /mgmt/admin/{id} request to the CA.
|
||||||
|
func (c *MgmtClient) RemoveAdmin(id string) error {
|
||||||
|
var retried bool
|
||||||
|
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)})
|
||||||
|
req, err := http.NewRequest("DELETE", u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "create DELETE %s request failed", u)
|
||||||
|
}
|
||||||
|
retry:
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "client DELETE %s failed", u)
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
if !retried && c.retryOnError(resp) {
|
||||||
|
retried = true
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
return readError(resp.Body)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAdmin performs the PUT /mgmt/admin/{id} request to the CA.
|
||||||
|
func (c *MgmtClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*mgmt.Admin, error) {
|
||||||
|
var retried bool
|
||||||
|
body, err := json.Marshal(uar)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request")
|
||||||
|
}
|
||||||
|
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join("/mgmt/admin", id)})
|
||||||
|
req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "create PUT %s request failed", u)
|
||||||
|
}
|
||||||
|
retry:
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "client PUT %s failed", u)
|
||||||
|
}
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
if !retried && c.retryOnError(resp) {
|
||||||
|
retried = true
|
||||||
|
goto retry
|
||||||
|
}
|
||||||
|
return nil, readError(resp.Body)
|
||||||
|
}
|
||||||
|
var adm = new(mgmt.Admin)
|
||||||
|
if err := readJSON(resp.Body, adm); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error reading %s", u)
|
||||||
|
}
|
||||||
|
return adm, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetAdmins performs the GET /mgmt/admins request to the CA.
|
// GetAdmins performs the GET /mgmt/admins request to the CA.
|
||||||
func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) {
|
func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) {
|
||||||
var retried bool
|
var retried bool
|
||||||
|
|
Loading…
Reference in a new issue