2021-05-03 19:48:20 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/go-chi/chi"
|
2022-03-30 08:22:22 +00:00
|
|
|
|
2022-03-18 13:28:42 +00:00
|
|
|
"go.step.sm/linkedca"
|
|
|
|
|
2021-05-03 19:48:20 +00:00
|
|
|
"github.com/smallstep/certificates/api"
|
2022-03-18 13:28:42 +00:00
|
|
|
"github.com/smallstep/certificates/api/read"
|
2022-03-30 08:22:22 +00:00
|
|
|
"github.com/smallstep/certificates/api/render"
|
2021-05-03 19:48:20 +00:00
|
|
|
"github.com/smallstep/certificates/authority"
|
|
|
|
"github.com/smallstep/certificates/authority/admin"
|
|
|
|
"github.com/smallstep/certificates/authority/provisioner"
|
|
|
|
"github.com/smallstep/certificates/errs"
|
|
|
|
)
|
|
|
|
|
|
|
|
// GetProvisionersResponse is the type for GET /admin/provisioners responses.
|
|
|
|
type GetProvisionersResponse struct {
|
|
|
|
Provisioners provisioner.List `json:"provisioners"`
|
|
|
|
NextCursor string `json:"nextCursor"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetProvisioner returns the requested provisioner, or an error.
|
|
|
|
func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
id := r.URL.Query().Get("id")
|
|
|
|
name := chi.URLParam(r, "name")
|
|
|
|
|
|
|
|
var (
|
|
|
|
p provisioner.Interface
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
if len(id) > 0 {
|
|
|
|
if p, err = h.auth.LoadProvisionerByID(id); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", id))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if p, err = h.auth.LoadProvisionerByName(name); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-08 12:26:30 +00:00
|
|
|
prov, err := h.adminDB.GetProvisioner(ctx, p.GetID())
|
2021-05-03 19:48:20 +00:00
|
|
|
if err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, err)
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-30 08:22:22 +00:00
|
|
|
render.ProtoJSON(w, prov)
|
2021-05-03 19:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetProvisioners returns the given segment of provisioners associated with the authority.
|
|
|
|
func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) {
|
|
|
|
cursor, limit, err := api.ParseCursor(r)
|
|
|
|
if err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err,
|
2021-12-09 22:15:38 +00:00
|
|
|
"error parsing cursor and limit from query params"))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
p, next, err := h.auth.GetProvisioners(cursor, limit)
|
|
|
|
if err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, errs.InternalServerErr(err))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-30 08:22:22 +00:00
|
|
|
render.JSON(w, &GetProvisionersResponse{
|
2021-05-03 19:48:20 +00:00
|
|
|
Provisioners: p,
|
|
|
|
NextCursor: next,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateProvisioner creates a new prov.
|
|
|
|
func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var prov = new(linkedca.Provisioner)
|
2022-03-18 13:28:42 +00:00
|
|
|
if err := read.ProtoJSON(r.Body, prov); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, err)
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Validate inputs
|
|
|
|
if err := authority.ValidateClaims(prov.Claims); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, err)
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := h.auth.StoreProvisioner(r.Context(), prov); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-30 08:22:22 +00:00
|
|
|
render.ProtoJSONStatus(w, prov, http.StatusCreated)
|
2021-05-03 19:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteProvisioner deletes a provisioner.
|
|
|
|
func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) {
|
|
|
|
id := r.URL.Query().Get("id")
|
|
|
|
name := chi.URLParam(r, "name")
|
|
|
|
|
|
|
|
var (
|
|
|
|
p provisioner.Interface
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
if len(id) > 0 {
|
|
|
|
if p, err = h.auth.LoadProvisionerByID(id); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", id))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if p, err = h.auth.LoadProvisionerByName(name); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", name))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := h.auth.RemoveProvisioner(r.Context(), p.GetID()); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error removing provisioner %s", p.GetName()))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-03-30 08:22:22 +00:00
|
|
|
render.JSON(w, &DeleteResponse{Status: "ok"})
|
2021-05-03 19:48:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateProvisioner updates an existing prov.
|
|
|
|
func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var nu = new(linkedca.Provisioner)
|
2022-03-18 13:28:42 +00:00
|
|
|
if err := read.ProtoJSON(r.Body, nu); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, err)
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
name := chi.URLParam(r, "name")
|
|
|
|
_old, err := h.auth.LoadProvisionerByName(name)
|
|
|
|
if err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner from cached configuration '%s'", name))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-02-08 12:26:30 +00:00
|
|
|
old, err := h.adminDB.GetProvisioner(r.Context(), _old.GetID())
|
2021-05-03 19:48:20 +00:00
|
|
|
if err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner from db '%s'", _old.GetID()))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if nu.Id != old.Id {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.NewErrorISE("cannot change provisioner ID"))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if nu.Type != old.Type {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.NewErrorISE("cannot change provisioner type"))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if nu.AuthorityId != old.AuthorityId {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.NewErrorISE("cannot change provisioner authorityID"))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if !nu.CreatedAt.AsTime().Equal(old.CreatedAt.AsTime()) {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.NewErrorISE("cannot change provisioner createdAt"))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if !nu.DeletedAt.AsTime().Equal(old.DeletedAt.AsTime()) {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, admin.NewErrorISE("cannot change provisioner deletedAt"))
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Validate inputs
|
|
|
|
if err := authority.ValidateClaims(nu.Claims); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, err)
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := h.auth.UpdateProvisioner(r.Context(), nu); err != nil {
|
2022-03-30 08:22:22 +00:00
|
|
|
render.Error(w, err)
|
2021-05-03 19:48:20 +00:00
|
|
|
return
|
|
|
|
}
|
2022-03-30 08:22:22 +00:00
|
|
|
render.ProtoJSON(w, nu)
|
2021-05-03 19:48:20 +00:00
|
|
|
}
|