Addressing comments in PR review

- added a bit of validation to admin create and update
- using protojson where possible in admin api
- fixing a few instances of admin -> acme in errors
This commit is contained in:
max furman 2021-07-06 17:14:13 -07:00
parent 5679c9933d
commit 1df21b9b6a
7 changed files with 43 additions and 16 deletions

View file

@ -18,6 +18,17 @@ type CreateAdminRequest struct {
// Validate validates a new-admin request body. // Validate validates a new-admin request body.
func (car *CreateAdminRequest) Validate() error { 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")
}
return nil return nil
} }
@ -34,6 +45,11 @@ type UpdateAdminRequest struct {
// Validate validates a new-admin request body. // Validate validates a new-admin request body.
func (uar *UpdateAdminRequest) Validate() error { 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")
}
return nil return nil
} }
@ -52,7 +68,7 @@ func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) {
"admin %s not found", id)) "admin %s not found", id))
return return
} }
api.JSON(w, adm) api.ProtoJSON(w, adm)
} }
// GetAdmins returns a segment of admins associated with the authority. // GetAdmins returns a segment of admins associated with the authority.
@ -104,7 +120,7 @@ func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request) {
return return
} }
api.JSON(w, adm) api.ProtoJSONStatus(w, adm, http.StatusCreated)
} }
// DeleteAdmin deletes admin. // DeleteAdmin deletes admin.
@ -127,6 +143,11 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
return return
} }
if err := body.Validate(); err != nil {
api.WriteError(w, err)
return
}
id := chi.URLParam(r, "id") id := chi.URLParam(r, "id")
adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type}) adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type})
@ -135,5 +156,5 @@ func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) {
return return
} }
api.JSON(w, adm) api.ProtoJSON(w, adm)
} }

View file

@ -14,7 +14,7 @@ type nextHTTP = func(http.ResponseWriter, *http.Request)
// is enabled before servicing requests. // is enabled before servicing requests.
func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP { func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if h.db == nil { if !h.auth.IsAdminAPIEnabled() {
api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType, api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType,
"administration API not enabled")) "administration API not enabled"))
return return

View file

@ -171,5 +171,5 @@ func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) {
api.WriteError(w, err) api.WriteError(w, err)
return return
} }
api.ProtoJSONStatus(w, nu, http.StatusOK) api.ProtoJSON(w, nu)
} }

View file

@ -15,7 +15,7 @@ var (
provisionersTable = []byte("provisioners") provisionersTable = []byte("provisioners")
) )
// DB is a struct that implements the AcmeDB interface. // DB is a struct that implements the AdminDB interface.
type DB struct { type DB struct {
db nosqlDB.DB db nosqlDB.DB
authorityID string authorityID string
@ -54,7 +54,7 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface
} else { } else {
oldB, err = json.Marshal(old) oldB, err = json.Marshal(old)
if err != nil { if err != nil {
return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, old) return errors.Wrapf(err, "error marshaling admin type: %s, value: %v", typ, old)
} }
} }

View file

@ -12,7 +12,7 @@ import (
"github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/logging"
) )
// ProblemType is the type of the ACME problem. // ProblemType is the type of the Admin problem.
type ProblemType int type ProblemType int
const ( const (
@ -33,7 +33,7 @@ const (
ErrorServerInternalType ErrorServerInternalType
) )
// String returns the string representation of the acme problem type, // String returns the string representation of the admin problem type,
// fulfilling the Stringer interface. // fulfilling the Stringer interface.
func (ap ProblemType) String() string { func (ap ProblemType) String() string {
switch ap { switch ap {
@ -73,17 +73,17 @@ var (
ErrorNotFoundType: { ErrorNotFoundType: {
typ: ErrorNotFoundType.String(), typ: ErrorNotFoundType.String(),
details: "resource not found", details: "resource not found",
status: 400, status: http.StatusNotFound,
}, },
ErrorAuthorityMismatchType: { ErrorAuthorityMismatchType: {
typ: ErrorAuthorityMismatchType.String(), typ: ErrorAuthorityMismatchType.String(),
details: "resource not owned by authority", details: "resource not owned by authority",
status: 401, status: http.StatusUnauthorized,
}, },
ErrorDeletedType: { ErrorDeletedType: {
typ: ErrorDeletedType.String(), typ: ErrorDeletedType.String(),
details: "resource is deleted", details: "resource is deleted",
status: http.StatusUnauthorized, status: http.StatusNotFound,
}, },
ErrorNotImplementedType: { ErrorNotImplementedType: {
typ: ErrorNotImplementedType.String(), typ: ErrorNotImplementedType.String(),
@ -104,7 +104,7 @@ var (
} }
) )
// Error represents an ACME // Error represents an Admin
type Error struct { type Error struct {
Type string `json:"type"` Type string `json:"type"`
Detail string `json:"detail"` Detail string `json:"detail"`

View file

@ -508,6 +508,12 @@ func (a *Authority) GetAdminDatabase() admin.DB {
return a.adminDB return a.adminDB
} }
// IsAdminAPIEnabled returns a boolean indicating whether the Admin API has
// been enabled.
func (a *Authority) IsAdminAPIEnabled() bool {
return a.config.AuthorityConfig.EnableAdmin
}
// Shutdown safely shuts down any clients, databases, etc. held by the Authority. // Shutdown safely shuts down any clients, databases, etc. held by the Authority.
func (a *Authority) Shutdown() error { func (a *Authority) Shutdown() error {
if err := a.keyManager.Close(); err != nil { if err := a.keyManager.Close(); err != nil {

View file

@ -132,7 +132,7 @@ retry:
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var adm = new(linkedca.Admin) var adm = new(linkedca.Admin)
if err := readJSON(resp.Body, adm); err != nil { if err := readProtoJSON(resp.Body, adm); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) return nil, errors.Wrapf(err, "error reading %s", u)
} }
return adm, nil return adm, nil
@ -270,7 +270,7 @@ retry:
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var adm = new(linkedca.Admin) var adm = new(linkedca.Admin)
if err := readJSON(resp.Body, adm); err != nil { if err := readProtoJSON(resp.Body, adm); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) return nil, errors.Wrapf(err, "error reading %s", u)
} }
return adm, nil return adm, nil
@ -334,7 +334,7 @@ retry:
return nil, readAdminError(resp.Body) return nil, readAdminError(resp.Body)
} }
var adm = new(linkedca.Admin) var adm = new(linkedca.Admin)
if err := readJSON(resp.Body, adm); err != nil { if err := readProtoJSON(resp.Body, adm); err != nil {
return nil, errors.Wrapf(err, "error reading %s", u) return nil, errors.Wrapf(err, "error reading %s", u)
} }
return adm, nil return adm, nil