certificates/authority/admin/api/admin.go

183 lines
5.4 KiB
Go
Raw Normal View History

2021-05-03 19:48:20 +00:00
package api
import (
2021-12-22 14:30:40 +00:00
"context"
2021-05-03 19:48:20 +00:00
"net/http"
"github.com/go-chi/chi"
"go.step.sm/linkedca"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/api/read"
"github.com/smallstep/certificates/api/render"
2021-05-18 04:07:25 +00:00
"github.com/smallstep/certificates/authority/admin"
2021-12-22 14:30:40 +00:00
"github.com/smallstep/certificates/authority/provisioner"
2021-05-03 19:48:20 +00:00
)
2021-12-22 14:30:40 +00:00
type adminAuthority interface {
LoadProvisionerByName(string) (provisioner.Interface, error)
GetProvisioners(cursor string, limit int) (provisioner.List, string, error)
IsAdminAPIEnabled() bool
LoadAdminByID(id string) (*linkedca.Admin, bool)
GetAdmins(cursor string, limit int) ([]*linkedca.Admin, string, error)
StoreAdmin(ctx context.Context, adm *linkedca.Admin, prov provisioner.Interface) error
UpdateAdmin(ctx context.Context, id string, nu *linkedca.Admin) (*linkedca.Admin, error)
RemoveAdmin(ctx context.Context, id string) error
AuthorizeAdminToken(r *http.Request, token string) (*linkedca.Admin, error)
StoreProvisioner(ctx context.Context, prov *linkedca.Provisioner) error
LoadProvisionerByID(id string) (provisioner.Interface, error)
UpdateProvisioner(ctx context.Context, nu *linkedca.Provisioner) error
RemoveProvisioner(ctx context.Context, id string) error
}
2021-05-03 19:48:20 +00:00
// CreateAdminRequest represents the body for a CreateAdmin request.
type CreateAdminRequest struct {
2021-05-26 04:13:01 +00:00
Subject string `json:"subject"`
Provisioner string `json:"provisioner"`
Type linkedca.Admin_Type `json:"type"`
2021-05-03 19:48:20 +00:00
}
// Validate validates a new-admin request body.
func (car *CreateAdminRequest) Validate() error {
if car.Subject == "" {
return admin.NewError(admin.ErrorBadRequestType, "subject cannot be empty")
}
if car.Provisioner == "" {
return admin.NewError(admin.ErrorBadRequestType, "provisioner cannot be empty")
}
switch car.Type {
case linkedca.Admin_SUPER_ADMIN, linkedca.Admin_ADMIN:
default:
return admin.NewError(admin.ErrorBadRequestType, "invalid value for admin type")
}
2021-05-03 19:48:20 +00:00
return nil
}
2021-05-18 23:50:54 +00:00
// GetAdminsResponse for returning a list of admins.
type GetAdminsResponse struct {
2021-05-26 04:13:01 +00:00
Admins []*linkedca.Admin `json:"admins"`
NextCursor string `json:"nextCursor"`
2021-05-18 23:50:54 +00:00
}
2021-05-03 19:48:20 +00:00
// UpdateAdminRequest represents the body for a UpdateAdmin request.
type UpdateAdminRequest struct {
2021-05-26 04:13:01 +00:00
Type linkedca.Admin_Type `json:"type"`
2021-05-03 19:48:20 +00:00
}
// Validate validates a new-admin request body.
func (uar *UpdateAdminRequest) Validate() error {
switch uar.Type {
case linkedca.Admin_SUPER_ADMIN, linkedca.Admin_ADMIN:
default:
return admin.NewError(admin.ErrorBadRequestType, "invalid value for admin type")
}
2021-05-03 19:48:20 +00:00
return nil
}
2021-05-12 07:03:40 +00:00
// DeleteResponse is the resource for successful DELETE responses.
type DeleteResponse struct {
Status string `json:"status"`
}
2021-05-03 19:48:20 +00:00
// GetAdmin returns the requested admin, or an error.
func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
adm, ok := h.auth.LoadAdminByID(id)
2021-05-18 23:50:54 +00:00
if !ok {
render.Error(w, admin.NewError(admin.ErrorNotFoundType,
2021-05-18 23:50:54 +00:00
"admin %s not found", id))
2021-05-03 19:48:20 +00:00
return
}
render.ProtoJSON(w, adm)
2021-05-03 19:48:20 +00:00
}
// GetAdmins returns a segment of admins associated with the authority.
2021-05-03 19:48:20 +00:00
func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) {
2021-05-18 23:50:54 +00:00
cursor, limit, err := api.ParseCursor(r)
2021-05-03 19:48:20 +00:00
if err != nil {
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err,
2021-05-18 23:50:54 +00:00
"error parsing cursor and limit from query params"))
2021-05-03 19:48:20 +00:00
return
}
2021-05-18 23:50:54 +00:00
admins, nextCursor, err := h.auth.GetAdmins(cursor, limit)
if err != nil {
render.Error(w, admin.WrapErrorISE(err, "error retrieving paginated admins"))
return
}
render.JSON(w, &GetAdminsResponse{
2021-05-18 23:50:54 +00:00
Admins: admins,
NextCursor: nextCursor,
})
2021-05-03 19:48:20 +00:00
}
// CreateAdmin creates a new admin.
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
2021-05-12 07:03:40 +00:00
var body CreateAdminRequest
if err := read.JSON(r.Body, &body); err != nil {
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
2021-05-12 07:03:40 +00:00
return
}
if err := body.Validate(); err != nil {
render.Error(w, err)
2021-05-18 04:07:25 +00:00
return
}
2021-05-12 07:03:40 +00:00
p, err := h.auth.LoadProvisionerByName(body.Provisioner)
if err != nil {
render.Error(w, admin.WrapErrorISE(err, "error loading provisioner %s", body.Provisioner))
2021-05-18 23:50:54 +00:00
return
}
2021-05-26 04:13:01 +00:00
adm := &linkedca.Admin{
ProvisionerId: p.GetID(),
2021-05-18 23:50:54 +00:00
Subject: body.Subject,
Type: body.Type,
2021-05-12 07:03:40 +00:00
}
// Store to authority collection.
if err := h.auth.StoreAdmin(r.Context(), adm, p); err != nil {
render.Error(w, admin.WrapErrorISE(err, "error storing admin"))
2021-05-12 07:03:40 +00:00
return
}
render.ProtoJSONStatus(w, adm, http.StatusCreated)
2021-05-12 07:03:40 +00:00
}
// DeleteAdmin deletes admin.
func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
if err := h.auth.RemoveAdmin(r.Context(), id); err != nil {
render.Error(w, admin.WrapErrorISE(err, "error deleting admin %s", id))
2021-05-18 04:07:25 +00:00
return
}
render.JSON(w, &DeleteResponse{Status: "ok"})
2021-05-03 19:48:20 +00:00
}
// UpdateAdmin updates an existing admin.
func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
2021-05-12 07:03:40 +00:00
var body UpdateAdminRequest
if err := read.JSON(r.Body, &body); err != nil {
render.Error(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body"))
2021-05-12 07:03:40 +00:00
return
}
if err := body.Validate(); err != nil {
render.Error(w, err)
return
}
2021-05-12 07:03:40 +00:00
id := chi.URLParam(r, "id")
2021-05-07 00:03:12 +00:00
adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type})
if err != nil {
render.Error(w, admin.WrapErrorISE(err, "error updating admin %s", id))
2021-05-12 07:03:40 +00:00
return
}
render.ProtoJSON(w, adm)
2021-05-03 19:48:20 +00:00
}