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")
|
||||
}
|
||||
}
|
||||
|
||||
a.config.AuthorityConfig, err = mgmtAuthConfig.ToCertificates()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -5,12 +5,13 @@ import (
|
|||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/authority/mgmt"
|
||||
)
|
||||
|
||||
// CreateAdminRequest represents the body for a CreateAdmin request.
|
||||
type CreateAdminRequest struct {
|
||||
Name string `json:"name"`
|
||||
Provisioner string `json:"provisioner"`
|
||||
ProvisionerID string `json:"provisionerID"`
|
||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||
}
|
||||
|
||||
|
@ -22,8 +23,9 @@ func (car *CreateAdminRequest) Validate() error {
|
|||
// UpdateAdminRequest represents the body for a UpdateAdmin request.
|
||||
type UpdateAdminRequest struct {
|
||||
Name string `json:"name"`
|
||||
Provisioner string `json:"provisioner"`
|
||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||
ProvisionerID string `json:"provisionerID"`
|
||||
IsSuperAdmin string `json:"isSuperAdmin"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// Validate validates a new-admin request body.
|
||||
|
@ -31,6 +33,11 @@ func (uar *UpdateAdminRequest) Validate() error {
|
|||
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.
|
||||
func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
@ -58,59 +65,84 @@ func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// CreateAdmin creates a new admin.
|
||||
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
/*
|
||||
ctx := r.Context()
|
||||
|
||||
var body CreateAdminRequest
|
||||
if err := ReadJSON(r.Body, &body); err != nil {
|
||||
api.WriteError(w, err)
|
||||
if err := api.ReadJSON(r.Body, &body); err != nil {
|
||||
api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body"))
|
||||
return
|
||||
}
|
||||
if err := body.Validate(); err != nil {
|
||||
api.WriteError(w, err)
|
||||
}
|
||||
|
||||
adm := &config.Admin{
|
||||
// TODO validate
|
||||
|
||||
adm := &mgmt.Admin{
|
||||
ProvisionerID: body.ProvisionerID,
|
||||
Name: body.Name,
|
||||
Provisioner: body.Provisioner,
|
||||
IsSuperAdmin: body.IsSuperAdmin,
|
||||
Status: mgmt.StatusActive,
|
||||
}
|
||||
if err := h.db.CreateAdmin(ctx, adm); err != nil {
|
||||
api.WriteError(w, err)
|
||||
api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin"))
|
||||
return
|
||||
}
|
||||
api.JSONStatus(w, adm, http.StatusCreated)
|
||||
*/
|
||||
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.
|
||||
func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
/*
|
||||
ctx := r.Context()
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
var body UpdateAdminRequest
|
||||
if err := ReadJSON(r.Body, &body); err != nil {
|
||||
api.WriteError(w, err)
|
||||
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)
|
||||
if err := api.ReadJSON(r.Body, &body); err != nil {
|
||||
api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body"))
|
||||
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.Provisioner = body.Provisioner
|
||||
adm.IsSuperAdmin = body.IsSuperAdmin
|
||||
|
||||
}
|
||||
if len(body.Status) > 0 {
|
||||
adm.Status = mgmt.StatusActive // FIXME
|
||||
}
|
||||
// Set IsSuperAdmin iff the string was set in the update request.
|
||||
if len(body.IsSuperAdmin) > 0 {
|
||||
adm.IsSuperAdmin = (body.IsSuperAdmin == "true")
|
||||
}
|
||||
if len(body.ProvisionerID) > 0 {
|
||||
adm.ProvisionerID = body.ProvisionerID
|
||||
}
|
||||
if err := h.db.UpdateAdmin(ctx, adm); err != nil {
|
||||
api.WriteError(w, err)
|
||||
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", "/provisioners", h.GetProvisioners)
|
||||
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
|
||||
r.MethodFunc("GET", "/admin/{id}", h.GetAdmin)
|
||||
r.MethodFunc("GET", "/admins", h.GetAdmins)
|
||||
r.MethodFunc("POST", "/admin", h.CreateAdmin)
|
||||
r.MethodFunc("PUT", "/admin/{id}", h.UpdateAdmin)
|
||||
r.MethodFunc("DELETE", "/admin/{id}", h.DeleteAdmin)
|
||||
|
||||
// AuthConfig
|
||||
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 {
|
||||
return errors.Wrap(err, "error generating random id for admin")
|
||||
}
|
||||
adm.AuthorityID = db.authorityID
|
||||
|
||||
dba := &dbAdmin{
|
||||
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
|
||||
// from the database.
|
||||
// TODO should we be paginating?
|
||||
func (db *DB) GetProvisioners(ctx context.Context) ([]*mgmt.Provisioner, error) {
|
||||
dbEntries, err := db.db.List(authorityProvisionersTable)
|
||||
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")
|
||||
}
|
||||
|
||||
details, err := json.Marshal(prov.Details)
|
||||
if err != nil {
|
||||
return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner")
|
||||
}
|
||||
|
||||
dbp := &dbProvisioner{
|
||||
ID: prov.ID,
|
||||
AuthorityID: db.authorityID,
|
||||
Type: prov.Type,
|
||||
Name: prov.Name,
|
||||
Claims: prov.Claims,
|
||||
Details: prov.Details,
|
||||
Details: details,
|
||||
X509Template: prov.X509Template,
|
||||
SSHTemplate: prov.SSHTemplate,
|
||||
CreatedAt: clock.Now(),
|
||||
|
@ -186,72 +190,44 @@ func (db *DB) UpdateProvisioner(ctx context.Context, prov *mgmt.Provisioner) err
|
|||
nu.DeletedAt = clock.Now()
|
||||
}
|
||||
nu.Claims = prov.Claims
|
||||
nu.Details = prov.Details
|
||||
nu.X509Template = prov.X509Template
|
||||
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)
|
||||
}
|
||||
|
||||
func unmarshalDetails(typ ProvisionerType, details []byte) (interface{}, error) {
|
||||
if !s.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
var v isProvisionerDetails_Data
|
||||
func unmarshalDetails(typ mgmt.ProvisionerType, data []byte) (mgmt.ProvisionerDetails, error) {
|
||||
var v mgmt.ProvisionerDetails
|
||||
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)
|
||||
case mgmt.ProvisionerTypeJWK:
|
||||
v = new(mgmt.ProvisionerDetailsJWK)
|
||||
case mgmt.ProvisionerTypeOIDC:
|
||||
v = new(mgmt.ProvisionerDetailsOIDC)
|
||||
case mgmt.ProvisionerTypeGCP:
|
||||
v = new(mgmt.ProvisionerDetailsGCP)
|
||||
case mgmt.ProvisionerTypeAWS:
|
||||
v = new(mgmt.ProvisionerDetailsAWS)
|
||||
case mgmt.ProvisionerTypeAZURE:
|
||||
v = new(mgmt.ProvisionerDetailsAzure)
|
||||
case mgmt.ProvisionerTypeACME:
|
||||
v = new(mgmt.ProvisionerDetailsACME)
|
||||
case mgmt.ProvisionerTypeX5C:
|
||||
v = new(mgmt.ProvisionerDetailsX5C)
|
||||
case mgmt.ProvisionerTypeK8SSA:
|
||||
v = new(mgmt.ProvisionerDetailsK8SSA)
|
||||
case mgmt.ProvisionerTypeSSHPOP:
|
||||
v = new(mgmt.ProvisionerDetailsSSHPOP)
|
||||
default:
|
||||
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 &ProvisionerDetails{Data: v}, nil
|
||||
return v, nil
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ const (
|
|||
ErrorAuthorityMismatchType
|
||||
// ErrorDeletedType resource has been deleted.
|
||||
ErrorDeletedType
|
||||
// ErrorBadRequestType bad request.
|
||||
ErrorBadRequestType
|
||||
// ErrorServerInternalType internal server error.
|
||||
ErrorServerInternalType
|
||||
)
|
||||
|
@ -37,6 +39,8 @@ func (ap ProblemType) String() string {
|
|||
return "authorityMismatch"
|
||||
case ErrorDeletedType:
|
||||
return "deleted"
|
||||
case ErrorBadRequestType:
|
||||
return "badRequest"
|
||||
case ErrorServerInternalType:
|
||||
return "internalServerError"
|
||||
default:
|
||||
|
@ -69,10 +73,15 @@ var (
|
|||
status: 401,
|
||||
},
|
||||
ErrorDeletedType: {
|
||||
typ: ErrorNotFoundType.String(),
|
||||
typ: ErrorDeletedType.String(),
|
||||
details: "resource is deleted",
|
||||
status: 403,
|
||||
},
|
||||
ErrorBadRequestType: {
|
||||
typ: ErrorBadRequestType.String(),
|
||||
details: "bad request",
|
||||
status: 400,
|
||||
},
|
||||
ErrorServerInternalType: errorServerInternalMetadata,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -23,12 +23,15 @@ type ProvisionerCtx struct {
|
|||
type ProvisionerType string
|
||||
|
||||
var (
|
||||
ProvisionerTypeJWK = ProvisionerType("JWK")
|
||||
ProvisionerTypeOIDC = ProvisionerType("OIDC")
|
||||
ProvisionerTypeACME = ProvisionerType("ACME")
|
||||
ProvisionerTypeX5C = ProvisionerType("X5C")
|
||||
ProvisionerTypeK8S = ProvisionerType("K8S")
|
||||
ProvisionerTypeAWS = ProvisionerType("AWS")
|
||||
ProvisionerTypeAZURE = ProvisionerType("AZURE")
|
||||
ProvisionerTypeGCP = ProvisionerType("GCP")
|
||||
ProvisionerTypeJWK = ProvisionerType("JWK")
|
||||
ProvisionerTypeK8SSA = ProvisionerType("K8SSA")
|
||||
ProvisionerTypeOIDC = ProvisionerType("OIDC")
|
||||
ProvisionerTypeSSHPOP = ProvisionerType("SSHPOP")
|
||||
ProvisionerTypeX5C = ProvisionerType("X5C")
|
||||
)
|
||||
|
||||
func NewProvisionerCtx(opts ...ProvisionerOption) *ProvisionerCtx {
|
||||
|
@ -56,8 +59,8 @@ func WithPassword(pass string) func(*ProvisionerCtx) {
|
|||
|
||||
// Provisioner type.
|
||||
type Provisioner struct {
|
||||
ID string `json:"-"`
|
||||
AuthorityID string `json:"-"`
|
||||
ID string `json:"id"`
|
||||
AuthorityID string `json:"authorityID"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Claims *Claims `json:"claims"`
|
||||
|
@ -108,6 +111,10 @@ func CreateProvisioner(ctx context.Context, db DB, typ, name string, opts ...Pro
|
|||
return p, nil
|
||||
}
|
||||
|
||||
type ProvisionerDetails interface {
|
||||
isProvisionerDetails()
|
||||
}
|
||||
|
||||
// ProvisionerDetailsJWK represents the values required by a JWK provisioner.
|
||||
type ProvisionerDetailsJWK struct {
|
||||
Type ProvisionerType `json:"type"`
|
||||
|
@ -115,6 +122,64 @@ type ProvisionerDetailsJWK struct {
|
|||
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) {
|
||||
var err error
|
||||
|
||||
|
@ -159,10 +224,6 @@ func (p *Provisioner) ToCertificates() (provisioner.Interface, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch details := p.Details.(type) {
|
||||
case *ProvisionerDetailsJWK:
|
||||
jwk := new(jose.JSONWebKey)
|
||||
|
@ -325,36 +386,3 @@ func (c *Claims) ToCertificates() (*provisioner.Claims, error) {
|
|||
EnableSSHCA: &c.SSH.Enabled,
|
||||
}, 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)
|
||||
}
|
||||
|
||||
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
|
||||
// request will be retried once.
|
||||
type RetryFunc func(code int) bool
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package ca
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"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.
|
||||
|
@ -83,6 +87,87 @@ retry:
|
|||
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.
|
||||
func (c *MgmtClient) GetAdmins() ([]*mgmt.Admin, error) {
|
||||
var retried bool
|
||||
|
|
Loading…
Reference in a new issue