diff --git a/.golangci.yml b/.golangci.yml index 1bab3ba3..92af7723 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,7 +8,7 @@ linters-settings: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf - golint: + revive: min-confidence: 0 gocyclo: min-complexity: 10 @@ -44,7 +44,7 @@ linters: disable-all: true enable: - gofmt - - golint + - revive - govet - misspell - ineffassign diff --git a/acme/api/middleware.go b/acme/api/middleware.go index 50f7146f..b2244dd7 100644 --- a/acme/api/middleware.go +++ b/acme/api/middleware.go @@ -288,13 +288,13 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - name := chi.URLParam(r, "provisionerID") - provID, err := url.PathUnescape(name) + nameEscaped := chi.URLParam(r, "provisionerID") + name, err := url.PathUnescape(nameEscaped) if err != nil { - api.WriteError(w, acme.WrapErrorISE(err, "error url unescaping provisioner id '%s'", name)) + api.WriteError(w, acme.WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped)) return } - p, err := h.ca.LoadProvisionerByID("acme/" + provID) + p, err := h.ca.LoadProvisionerByName(name) if err != nil { api.WriteError(w, err) return diff --git a/acme/common.go b/acme/common.go index 26552c61..f18907fe 100644 --- a/acme/common.go +++ b/acme/common.go @@ -11,7 +11,7 @@ import ( // CertificateAuthority is the interface implemented by a CA authority. type CertificateAuthority interface { Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) } // Clock that returns time in UTC rounded to seconds. diff --git a/acme/db/nosql/nosql.go b/acme/db/nosql/nosql.go index e2edd050..052f5729 100644 --- a/acme/db/nosql/nosql.go +++ b/acme/db/nosql/nosql.go @@ -23,12 +23,11 @@ var ( // DB is a struct that implements the AcmeDB interface. type DB struct { - db nosqlDB.DB - authorityID string + db nosqlDB.DB } // New configures and returns a new ACME DB backend implemented using a nosql DB. -func New(db nosqlDB.DB, authorityID string) (*DB, error) { +func New(db nosqlDB.DB) (*DB, error) { tables := [][]byte{accountTable, accountByKeyIDTable, authzTable, challengeTable, nonceTable, orderTable, ordersByAccountIDTable, certTable} for _, b := range tables { @@ -37,7 +36,7 @@ func New(db nosqlDB.DB, authorityID string) (*DB, error) { string(b)) } } - return &DB{db, authorityID}, nil + return &DB{db}, nil } // save writes the new data to the database, overwriting the old data if it diff --git a/acme/order_test.go b/acme/order_test.go index 993a92f2..849f0daa 100644 --- a/acme/order_test.go +++ b/acme/order_test.go @@ -261,10 +261,10 @@ func TestOrder_UpdateStatus(t *testing.T) { } type mockSignAuth struct { - sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - loadProvisionerByID func(string) (provisioner.Interface, error) - ret1, ret2 interface{} - err error + sign func(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) + loadProvisionerByName func(string) (provisioner.Interface, error) + ret1, ret2 interface{} + err error } func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { @@ -276,9 +276,9 @@ func (m *mockSignAuth) Sign(csr *x509.CertificateRequest, signOpts provisioner.S return []*x509.Certificate{m.ret1.(*x509.Certificate), m.ret2.(*x509.Certificate)}, m.err } -func (m *mockSignAuth) LoadProvisionerByID(id string) (provisioner.Interface, error) { - if m.loadProvisionerByID != nil { - return m.loadProvisionerByID(id) +func (m *mockSignAuth) LoadProvisionerByName(name string) (provisioner.Interface, error) { + if m.loadProvisionerByName != nil { + return m.loadProvisionerByName(name) } return m.ret1.(provisioner.Interface), m.err } diff --git a/api/api.go b/api/api.go index 20ef7e14..b542f473 100644 --- a/api/api.go +++ b/api/api.go @@ -39,7 +39,7 @@ type Authority interface { Renew(peer *x509.Certificate) ([]*x509.Certificate, error) Rekey(peer *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) LoadProvisionerByCertificate(*x509.Certificate) (provisioner.Interface, error) - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) GetProvisioners(cursor string, limit int) (provisioner.List, string, error) Revoke(context.Context, *authority.RevokeOptions) error GetEncryptedKey(kid string) (string, error) @@ -418,7 +418,7 @@ func LogCertificate(w http.ResponseWriter, cert *x509.Certificate) { if len(val.CredentialID) > 0 { m["provisioner"] = fmt.Sprintf("%s (%s)", val.Name, val.CredentialID) } else { - m["provisioner"] = fmt.Sprintf("%s", val.Name) + m["provisioner"] = string(val.Name) } break } diff --git a/api/api_test.go b/api/api_test.go index 62ef7740..33d2bae7 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -430,6 +430,7 @@ type mockProvisioner struct { ret1, ret2, ret3 interface{} err error getID func() string + getIDForToken func() string getTokenID func(string) (string, error) getName func() string getType func() provisioner.Type @@ -452,6 +453,13 @@ func (m *mockProvisioner) GetID() string { return m.ret1.(string) } +func (m *mockProvisioner) GetIDForToken() string { + if m.getIDForToken != nil { + return m.getIDForToken() + } + return m.ret1.(string) +} + func (m *mockProvisioner) GetTokenID(token string) (string, error) { if m.getTokenID != nil { return m.getTokenID(token) @@ -553,7 +561,7 @@ type mockAuthority struct { renew func(cert *x509.Certificate) ([]*x509.Certificate, error) rekey func(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) loadProvisionerByCertificate func(cert *x509.Certificate) (provisioner.Interface, error) - loadProvisionerByID func(provID string) (provisioner.Interface, error) + loadProvisionerByName func(name string) (provisioner.Interface, error) getProvisioners func(nextCursor string, limit int) (provisioner.List, string, error) revoke func(context.Context, *authority.RevokeOptions) error getEncryptedKey func(kid string) (string, error) @@ -633,9 +641,9 @@ func (m *mockAuthority) LoadProvisionerByCertificate(cert *x509.Certificate) (pr return m.ret1.(provisioner.Interface), m.err } -func (m *mockAuthority) LoadProvisionerByID(provID string) (provisioner.Interface, error) { - if m.loadProvisionerByID != nil { - return m.loadProvisionerByID(provID) +func (m *mockAuthority) LoadProvisionerByName(name string) (provisioner.Interface, error) { + if m.loadProvisionerByName != nil { + return m.loadProvisionerByName(name) } return m.ret1.(provisioner.Interface), m.err } diff --git a/api/errors.go b/api/errors.go index f9bcb199..db3bc3e2 100644 --- a/api/errors.go +++ b/api/errors.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/acme" - "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/scep" @@ -20,8 +20,8 @@ func WriteError(w http.ResponseWriter, err error) { case *acme.Error: acme.WriteError(w, k) return - case *mgmt.Error: - mgmt.WriteError(w, k) + case *admin.Error: + admin.WriteError(w, k) return case *scep.Error: w.Header().Set("Content-Type", "text/plain") diff --git a/api/utils.go b/api/utils.go index 0d87a065..bf45db53 100644 --- a/api/utils.go +++ b/api/utils.go @@ -3,11 +3,14 @@ package api import ( "encoding/json" "io" + "io/ioutil" "log" "net/http" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" ) // EnableLogger is an interface that enables response logging for an object. @@ -64,6 +67,29 @@ func JSONStatus(w http.ResponseWriter, v interface{}, status int) { LogEnabledResponse(w, v) } +// ProtoJSON writes the passed value into the http.ResponseWriter. +func ProtoJSON(w http.ResponseWriter, m proto.Message) { + ProtoJSONStatus(w, m, http.StatusOK) +} + +// ProtoJSONStatus writes the given value into the http.ResponseWriter and the +// given status is written as the status code of the response. +func ProtoJSONStatus(w http.ResponseWriter, m proto.Message, status int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + + b, err := protojson.Marshal(m) + if err != nil { + LogError(w, err) + return + } + if _, err := w.Write(b); err != nil { + LogError(w, err) + return + } + //LogEnabledResponse(w, v) +} + // ReadJSON reads JSON from the request body and stores it in the value // pointed by v. func ReadJSON(r io.Reader, v interface{}) error { @@ -72,3 +98,13 @@ func ReadJSON(r io.Reader, v interface{}) error { } return nil } + +// ReadProtoJSON reads JSON from the request body and stores it in the value +// pointed by v. +func ReadProtoJSON(r io.Reader, m proto.Message) error { + data, err := ioutil.ReadAll(r) + if err != nil { + return errs.Wrap(http.StatusBadRequest, err, "error reading request body") + } + return protojson.Unmarshal(data, m) +} diff --git a/authority/mgmt/api/admin.go b/authority/admin/api/admin.go similarity index 50% rename from authority/mgmt/api/admin.go rename to authority/admin/api/admin.go index 30c78555..d92af5b2 100644 --- a/authority/mgmt/api/admin.go +++ b/authority/admin/api/admin.go @@ -1,14 +1,12 @@ package api import ( - "fmt" "net/http" "github.com/go-chi/chi" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/admin" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/linkedca" + "go.step.sm/linkedca" ) // CreateAdminRequest represents the body for a CreateAdmin request. @@ -19,11 +17,7 @@ type CreateAdminRequest struct { } // Validate validates a new-admin request body. -func (car *CreateAdminRequest) Validate(c *admin.Collection) error { - if _, ok := c.LoadBySubProv(car.Subject, car.Provisioner); ok { - return mgmt.NewError(mgmt.ErrorBadRequestType, - "admin with subject: '%s' and provisioner: '%s' already exists", car.Subject, car.Provisioner) - } +func (car *CreateAdminRequest) Validate() error { return nil } @@ -52,25 +46,29 @@ type DeleteResponse struct { func (h *Handler) GetAdmin(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - adm, ok := h.auth.GetAdminCollection().LoadByID(id) + adm, ok := h.auth.LoadAdminByID(id) if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, + api.WriteError(w, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id)) return } api.JSON(w, adm) } -// GetAdmins returns all admins associated with the authority. +// GetAdmins returns a segment of admins associated with the authority. func (h *Handler) GetAdmins(w http.ResponseWriter, r *http.Request) { cursor, limit, err := api.ParseCursor(r) if err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error parsing cursor and limit from query params")) return } - admins, nextCursor := h.auth.GetAdminCollection().Find(cursor, limit) + admins, nextCursor, err := h.auth.GetAdmins(cursor, limit) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error retrieving paginated admins")) + return + } api.JSON(w, &GetAdminsResponse{ Admins: admins, NextCursor: nextCursor, @@ -79,91 +77,63 @@ 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 := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body")) + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body")) return } - if err := body.Validate(h.auth.GetAdminCollection()); err != nil { + if err := body.Validate(); err != nil { api.WriteError(w, err) return } - p, ok := h.auth.GetProvisionerCollection().LoadByName(body.Provisioner) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", body.Provisioner)) + p, err := h.auth.LoadProvisionerByName(body.Provisioner) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", body.Provisioner)) return } - adm := &linkedca.Admin{ ProvisionerId: p.GetID(), Subject: body.Subject, Type: body.Type, } - if err := h.db.CreateAdmin(ctx, adm); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error creating admin")) + // Store to authority collection. + if err := h.auth.StoreAdmin(r.Context(), adm, p); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error storing admin")) return } + api.JSON(w, adm) - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } } // DeleteAdmin deletes admin. func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") - if h.auth.GetAdminCollection().SuperCount() == 1 { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "cannot remove the last super admin")) + if err := h.auth.RemoveAdmin(r.Context(), id); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error deleting admin %s", id)) return } - ctx := r.Context() - if err := h.db.DeleteAdmin(ctx, id); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s", id)) - return - } api.JSON(w, &DeleteResponse{Status: "ok"}) - - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } } // UpdateAdmin updates an existing admin. func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - var body UpdateAdminRequest if err := api.ReadJSON(r.Body, &body); err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, "error reading request body")) + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body")) return } id := chi.URLParam(r, "id") - adm, ok := h.auth.GetAdminCollection().LoadByID(id) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id)) - return - } - if adm.Type == body.Type { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type)) + adm, err := h.auth.UpdateAdmin(r.Context(), id, &linkedca.Admin{Type: body.Type}) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error updating admin %s", id)) return } - adm.Type = body.Type - - if err := h.db.UpdateAdmin(ctx, (*linkedca.Admin)(adm)); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error updating admin %s", id)) - return - } api.JSON(w, adm) - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } } diff --git a/authority/admin/api/handler.go b/authority/admin/api/handler.go new file mode 100644 index 00000000..d88edfa1 --- /dev/null +++ b/authority/admin/api/handler.go @@ -0,0 +1,41 @@ +package api + +import ( + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/admin" +) + +// Handler is the ACME API request handler. +type Handler struct { + db admin.DB + auth *authority.Authority +} + +// NewHandler returns a new Authority Config Handler. +func NewHandler(auth *authority.Authority) api.RouterHandler { + h := &Handler{db: auth.GetAdminDatabase(), auth: auth} + + return h +} + +// Route traffic and implement the Router interface. +func (h *Handler) Route(r api.Router) { + authnz := func(next nextHTTP) nextHTTP { + return h.extractAuthorizeTokenAdmin(h.requireAPIEnabled(next)) + } + + // Provisioners + r.MethodFunc("GET", "/provisioners/{name}", authnz(h.GetProvisioner)) + r.MethodFunc("GET", "/provisioners", authnz(h.GetProvisioners)) + r.MethodFunc("POST", "/provisioners", authnz(h.CreateProvisioner)) + r.MethodFunc("PUT", "/provisioners/{name}", authnz(h.UpdateProvisioner)) + r.MethodFunc("DELETE", "/provisioners/{name}", authnz(h.DeleteProvisioner)) + + // Admins + r.MethodFunc("GET", "/admins/{id}", authnz(h.GetAdmin)) + r.MethodFunc("GET", "/admins", authnz(h.GetAdmins)) + r.MethodFunc("POST", "/admins", authnz(h.CreateAdmin)) + r.MethodFunc("PATCH", "/admins/{id}", authnz(h.UpdateAdmin)) + r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin)) +} diff --git a/authority/admin/api/middleware.go b/authority/admin/api/middleware.go new file mode 100644 index 00000000..137bd6f7 --- /dev/null +++ b/authority/admin/api/middleware.go @@ -0,0 +1,66 @@ +package api + +import ( + "context" + "net/http" + + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority/admin" +) + +type nextHTTP = func(http.ResponseWriter, *http.Request) + +// requireAPIEnabled is a middleware that ensures the Administration API +// is enabled before servicing requests. +func (h *Handler) requireAPIEnabled(next nextHTTP) nextHTTP { + return func(w http.ResponseWriter, r *http.Request) { + if h.db == nil { + api.WriteError(w, admin.NewError(admin.ErrorNotImplementedType, + "administration API not enabled")) + return + } + next(w, r) + } +} + +// extractAuthorizeTokenAdmin is a middleware that extracts and caches the bearer token. +func (h *Handler) extractAuthorizeTokenAdmin(next nextHTTP) nextHTTP { + return func(w http.ResponseWriter, r *http.Request) { + tok := r.Header.Get("Authorization") + if len(tok) == 0 { + api.WriteError(w, admin.NewError(admin.ErrorUnauthorizedType, + "missing authorization header token")) + return + } + + adm, err := h.auth.AuthorizeAdminToken(r, tok) + if err != nil { + api.WriteError(w, err) + return + } + + ctx := context.WithValue(r.Context(), adminContextKey, adm) + next(w, r.WithContext(ctx)) + } +} + +// ContextKey is the key type for storing and searching for ACME request +// essentials in the context of a request. +type ContextKey string + +const ( + // adminContextKey account key + adminContextKey = ContextKey("admin") +) + +/* +// adminFromContext searches the context for the token. Returns the +// token or an error. +func adminFromContext(ctx context.Context) (*linkedca.Admin, error) { + val, ok := ctx.Value(adminContextKey).(*linkedca.Admin) + if !ok || val == nil { + return nil, admin.NewErrorISE("admin not in context") + } + return val, nil +} +*/ diff --git a/authority/admin/api/provisioner.go b/authority/admin/api/provisioner.go new file mode 100644 index 00000000..e63a4417 --- /dev/null +++ b/authority/admin/api/provisioner.go @@ -0,0 +1,175 @@ +package api + +import ( + "net/http" + + "github.com/go-chi/chi" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/authority" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/provisioner" + "github.com/smallstep/certificates/errs" + "go.step.sm/linkedca" +) + +// 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 { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", id)) + return + } + } else { + if p, err = h.auth.LoadProvisionerByName(name); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name)) + return + } + } + + prov, err := h.db.GetProvisioner(ctx, p.GetID()) + if err != nil { + api.WriteError(w, err) + return + } + api.ProtoJSON(w, prov) +} + +// 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 { + api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, + "error parsing cursor & limit query params")) + return + } + + p, next, err := h.auth.GetProvisioners(cursor, limit) + if err != nil { + api.WriteError(w, errs.InternalServerErr(err)) + return + } + api.JSON(w, &GetProvisionersResponse{ + Provisioners: p, + NextCursor: next, + }) +} + +// CreateProvisioner creates a new prov. +func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { + var prov = new(linkedca.Provisioner) + if err := api.ReadProtoJSON(r.Body, prov); err != nil { + api.WriteError(w, err) + return + } + + // TODO: Validate inputs + if err := authority.ValidateClaims(prov.Claims); err != nil { + api.WriteError(w, err) + return + } + + if err := h.auth.StoreProvisioner(r.Context(), prov); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error storing provisioner %s", prov.Name)) + return + } + api.ProtoJSONStatus(w, prov, http.StatusCreated) +} + +// 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 { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", id)) + return + } + } else { + if p, err = h.auth.LoadProvisionerByName(name); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner %s", name)) + return + } + } + + if err := h.auth.RemoveProvisioner(r.Context(), p.GetID()); err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error removing provisioner %s", p.GetName())) + return + } + + api.JSON(w, &DeleteResponse{Status: "ok"}) +} + +// UpdateProvisioner updates an existing prov. +func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { + var nu = new(linkedca.Provisioner) + if err := api.ReadProtoJSON(r.Body, nu); err != nil { + api.WriteError(w, err) + return + } + + name := chi.URLParam(r, "name") + _old, err := h.auth.LoadProvisionerByName(name) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner from cached configuration '%s'", name)) + return + } + + old, err := h.db.GetProvisioner(r.Context(), _old.GetID()) + if err != nil { + api.WriteError(w, admin.WrapErrorISE(err, "error loading provisioner from db '%s'", _old.GetID())) + return + } + + if nu.Id != old.Id { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner ID")) + return + } + if nu.Type != old.Type { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner type")) + return + } + if nu.AuthorityId != old.AuthorityId { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner authorityID")) + return + } + if !nu.CreatedAt.AsTime().Equal(old.CreatedAt.AsTime()) { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner createdAt")) + return + } + if !nu.DeletedAt.AsTime().Equal(old.DeletedAt.AsTime()) { + api.WriteError(w, admin.NewErrorISE("cannot change provisioner deletedAt")) + return + } + + // TODO: Validate inputs + if err := authority.ValidateClaims(nu.Claims); err != nil { + api.WriteError(w, err) + return + } + + if err := h.auth.UpdateProvisioner(r.Context(), nu); err != nil { + api.WriteError(w, err) + return + } + api.ProtoJSONStatus(w, nu, http.StatusOK) +} diff --git a/authority/admin/collection.go b/authority/admin/collection.go deleted file mode 100644 index e9d41113..00000000 --- a/authority/admin/collection.go +++ /dev/null @@ -1,180 +0,0 @@ -package admin - -import ( - "crypto/sha1" - "encoding/binary" - "encoding/hex" - "fmt" - "sort" - "strings" - "sync" - - "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/linkedca" -) - -// DefaultAdminLimit is the default limit for listing provisioners. -const DefaultAdminLimit = 20 - -// DefaultAdminMax is the maximum limit for listing provisioners. -const DefaultAdminMax = 100 - -type uidAdmin struct { - admin *linkedca.Admin - uid string -} - -type adminSlice []uidAdmin - -func (p adminSlice) Len() int { return len(p) } -func (p adminSlice) Less(i, j int) bool { return p[i].uid < p[j].uid } -func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// Collection is a memory map of admins. -type Collection struct { - byID *sync.Map - bySubProv *sync.Map - byProv *sync.Map - sorted adminSlice - provisioners *provisioner.Collection - superCount int - superCountByProvisioner map[string]int -} - -// NewCollection initializes a collection of provisioners. The given list of -// audiences are the audiences used by the JWT provisioner. -func NewCollection(provisioners *provisioner.Collection) *Collection { - return &Collection{ - byID: new(sync.Map), - byProv: new(sync.Map), - bySubProv: new(sync.Map), - superCountByProvisioner: map[string]int{}, - provisioners: provisioners, - } -} - -// LoadByID a admin by the ID. -func (c *Collection) LoadByID(id string) (*linkedca.Admin, bool) { - return loadAdmin(c.byID, id) -} - -func subProvNameHash(sub, provName string) string { - subHash := sha1.Sum([]byte(sub)) - provNameHash := sha1.Sum([]byte(provName)) - _res := sha1.Sum(append(subHash[:], provNameHash[:]...)) - return string(_res[:]) -} - -// LoadBySubProv a admin by the subject and provisioner name. -func (c *Collection) LoadBySubProv(sub, provName string) (*linkedca.Admin, bool) { - return loadAdmin(c.bySubProv, subProvNameHash(sub, provName)) -} - -// LoadByProvisioner a admin by the subject and provisioner name. -func (c *Collection) LoadByProvisioner(provName string) ([]*linkedca.Admin, bool) { - a, ok := c.byProv.Load(provName) - if !ok { - return nil, false - } - admins, ok := a.([]*linkedca.Admin) - if !ok { - return nil, false - } - return admins, true -} - -// Store adds an admin to the collection and enforces the uniqueness of -// admin IDs and amdin subject <-> provisioner name combos. -func (c *Collection) Store(adm *linkedca.Admin) error { - p, ok := c.provisioners.Load(adm.ProvisionerId) - if !ok { - return fmt.Errorf("provisioner %s not found", adm.ProvisionerId) - } - // Store admin always in byID. ID must be unique. - if _, loaded := c.byID.LoadOrStore(adm.Id, adm); loaded { - return errors.New("cannot add multiple admins with the same id") - } - - provName := p.GetName() - // Store admin always in bySubProv. Subject <-> ProvisionerName must be unique. - if _, loaded := c.bySubProv.LoadOrStore(subProvNameHash(adm.Subject, provName), adm); loaded { - c.byID.Delete(adm.Id) - return errors.New("cannot add multiple admins with the same subject and provisioner") - } - - if admins, ok := c.LoadByProvisioner(provName); ok { - c.byProv.Store(provName, append(admins, adm)) - c.superCountByProvisioner[provName]++ - } else { - c.byProv.Store(provName, []*linkedca.Admin{adm}) - c.superCountByProvisioner[provName] = 1 - } - c.superCount++ - - // Store sorted admins. - // Use the first 4 bytes (32bit) of the sum to insert the order - // Using big endian format to get the strings sorted: - // 0x00000000, 0x00000001, 0x00000002, ... - bi := make([]byte, 4) - _sum := sha1.Sum([]byte(adm.Id)) - sum := _sum[:] - binary.BigEndian.PutUint32(bi, uint32(c.sorted.Len())) - sum[0], sum[1], sum[2], sum[3] = bi[0], bi[1], bi[2], bi[3] - c.sorted = append(c.sorted, uidAdmin{ - admin: adm, - uid: hex.EncodeToString(sum), - }) - sort.Sort(c.sorted) - - return nil -} - -// SuperCount returns the total number of admins. -func (c *Collection) SuperCount() int { - return c.superCount -} - -// SuperCountByProvisioner returns the total number of admins. -func (c *Collection) SuperCountByProvisioner(provName string) int { - if cnt, ok := c.superCountByProvisioner[provName]; ok { - return cnt - } - return 0 -} - -// Find implements pagination on a list of sorted provisioners. -func (c *Collection) Find(cursor string, limit int) ([]*linkedca.Admin, string) { - switch { - case limit <= 0: - limit = DefaultAdminLimit - case limit > DefaultAdminMax: - limit = DefaultAdminMax - } - - n := c.sorted.Len() - cursor = fmt.Sprintf("%040s", cursor) - i := sort.Search(n, func(i int) bool { return c.sorted[i].uid >= cursor }) - - slice := []*linkedca.Admin{} - for ; i < n && len(slice) < limit; i++ { - slice = append(slice, c.sorted[i].admin) - } - - if i < n { - return slice, strings.TrimLeft(c.sorted[i].uid, "0") - } - return slice, "" -} - -func loadAdmin(m *sync.Map, key string) (*linkedca.Admin, bool) { - a, ok := m.Load(key) - if !ok { - return nil, false - } - adm, ok := a.(*linkedca.Admin) - if !ok { - return nil, false - } - return adm, true -} diff --git a/authority/mgmt/db.go b/authority/admin/db.go similarity index 72% rename from authority/mgmt/db.go rename to authority/admin/db.go index 64ed39a2..15fe6686 100644 --- a/authority/mgmt/db.go +++ b/authority/admin/db.go @@ -1,16 +1,59 @@ -package mgmt +package admin import ( "context" + "encoding/json" + "fmt" "github.com/pkg/errors" - "github.com/smallstep/certificates/linkedca" + "go.step.sm/linkedca" +) + +const ( + // DefaultAuthorityID is the default AuthorityID. This will be the ID + // of the first Authority created, as well as the default AuthorityID + // if one is not specified in the configuration. + DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" ) // ErrNotFound is an error that should be used by the authority.DB interface to // indicate that an entity does not exist. var ErrNotFound = errors.New("not found") +// UnmarshalProvisionerDetails unmarshals details type to the specific provisioner details. +func UnmarshalProvisionerDetails(typ linkedca.Provisioner_Type, data []byte) (*linkedca.ProvisionerDetails, error) { + var v linkedca.ProvisionerDetails + switch typ { + case linkedca.Provisioner_JWK: + v.Data = new(linkedca.ProvisionerDetails_JWK) + case linkedca.Provisioner_OIDC: + v.Data = new(linkedca.ProvisionerDetails_OIDC) + case linkedca.Provisioner_GCP: + v.Data = new(linkedca.ProvisionerDetails_GCP) + case linkedca.Provisioner_AWS: + v.Data = new(linkedca.ProvisionerDetails_AWS) + case linkedca.Provisioner_AZURE: + v.Data = new(linkedca.ProvisionerDetails_Azure) + case linkedca.Provisioner_ACME: + v.Data = new(linkedca.ProvisionerDetails_ACME) + case linkedca.Provisioner_X5C: + v.Data = new(linkedca.ProvisionerDetails_X5C) + case linkedca.Provisioner_K8SSA: + v.Data = new(linkedca.ProvisionerDetails_K8SSA) + case linkedca.Provisioner_SSHPOP: + v.Data = new(linkedca.ProvisionerDetails_SSHPOP) + case linkedca.Provisioner_SCEP: + v.Data = new(linkedca.ProvisionerDetails_SCEP) + default: + return nil, fmt.Errorf("unsupported provisioner type %s", typ) + } + + if err := json.Unmarshal(data, v.Data); err != nil { + return nil, err + } + return &linkedca.ProvisionerDetails{Data: v.Data}, nil +} + // DB is the DB interface expected by the step-ca ACME API. type DB interface { CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error diff --git a/authority/mgmt/db/nosql/admin.go b/authority/admin/db/nosql/admin.go similarity index 68% rename from authority/mgmt/db/nosql/admin.go rename to authority/admin/db/nosql/admin.go index 8fd0fea5..6bb6bdd1 100644 --- a/authority/mgmt/db/nosql/admin.go +++ b/authority/admin/db/nosql/admin.go @@ -6,9 +6,10 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/linkedca" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/nosql" + "go.step.sm/linkedca" + "google.golang.org/protobuf/types/known/timestamppb" ) // dbAdmin is the database representation of the Admin type. @@ -22,60 +23,66 @@ type dbAdmin struct { DeletedAt time.Time `json:"deletedAt"` } -func (dbp *dbAdmin) clone() *dbAdmin { - u := *dbp +func (dba *dbAdmin) convert() *linkedca.Admin { + return &linkedca.Admin{ + Id: dba.ID, + AuthorityId: dba.AuthorityID, + ProvisionerId: dba.ProvisionerID, + Subject: dba.Subject, + Type: dba.Type, + CreatedAt: timestamppb.New(dba.CreatedAt), + DeletedAt: timestamppb.New(dba.DeletedAt), + } +} + +func (dba *dbAdmin) clone() *dbAdmin { + u := *dba return &u } func (db *DB) getDBAdminBytes(ctx context.Context, id string) ([]byte, error) { - data, err := db.db.Get(authorityAdminsTable, []byte(id)) + data, err := db.db.Get(adminsTable, []byte(id)) if nosql.IsErrNotFound(err) { - return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "admin %s not found", id) + return nil, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id) } else if err != nil { return nil, errors.Wrapf(err, "error loading admin %s", id) } return data, nil } -func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { - data, err := db.getDBAdminBytes(ctx, id) - if err != nil { - return nil, err +func (db *DB) unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { + var dba = new(dbAdmin) + if err := json.Unmarshal(data, dba); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) } - dba, err := unmarshalDBAdmin(data, id) - if err != nil { - return nil, err + if !dba.DeletedAt.IsZero() { + return nil, admin.NewError(admin.ErrorDeletedType, "admin %s is deleted", id) } if dba.AuthorityID != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, + return nil, admin.NewError(admin.ErrorAuthorityMismatchType, "admin %s is not owned by authority %s", dba.ID, db.authorityID) } return dba, nil } -func unmarshalDBAdmin(data []byte, id string) (*dbAdmin, error) { - var dba = new(dbAdmin) - if err := json.Unmarshal(data, dba); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling admin %s into dbAdmin", id) +func (db *DB) getDBAdmin(ctx context.Context, id string) (*dbAdmin, error) { + data, err := db.getDBAdminBytes(ctx, id) + if err != nil { + return nil, err } - if !dba.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "admin %s is deleted", id) + dba, err := db.unmarshalDBAdmin(data, id) + if err != nil { + return nil, err } return dba, nil } -func unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) { - dba, err := unmarshalDBAdmin(data, id) +func (db *DB) unmarshalAdmin(data []byte, id string) (*linkedca.Admin, error) { + dba, err := db.unmarshalDBAdmin(data, id) if err != nil { return nil, err } - return &linkedca.Admin{ - Id: dba.ID, - AuthorityId: dba.AuthorityID, - ProvisionerId: dba.ProvisionerID, - Subject: dba.Subject, - Type: dba.Type, - }, nil + return dba.convert(), nil } // GetAdmin retrieves and unmarshals a admin from the database. @@ -84,14 +91,10 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) if err != nil { return nil, err } - adm, err := unmarshalAdmin(data, id) + adm, err := db.unmarshalAdmin(data, id) if err != nil { return nil, err } - if adm.AuthorityId != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "admin %s is not owned by authority %s", adm.Id, db.authorityID) - } return adm, nil } @@ -100,15 +103,24 @@ func (db *DB) GetAdmin(ctx context.Context, id string) (*linkedca.Admin, error) // from the database. // TODO should we be paginating? func (db *DB) GetAdmins(ctx context.Context) ([]*linkedca.Admin, error) { - dbEntries, err := db.db.List(authorityAdminsTable) + dbEntries, err := db.db.List(adminsTable) if err != nil { return nil, errors.Wrap(err, "error loading admins") } var admins = []*linkedca.Admin{} for _, entry := range dbEntries { - adm, err := unmarshalAdmin(entry.Value, string(entry.Key)) + adm, err := db.unmarshalAdmin(entry.Value, string(entry.Key)) if err != nil { - return nil, err + switch k := err.(type) { + case *admin.Error: + if k.IsType(admin.ErrorDeletedType) || k.IsType(admin.ErrorAuthorityMismatchType) { + continue + } else { + return nil, err + } + default: + return nil, err + } } if adm.AuthorityId != db.authorityID { continue @@ -123,7 +135,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { var err error adm.Id, err = randID() if err != nil { - return mgmt.WrapErrorISE(err, "error generating random id for admin") + return admin.WrapErrorISE(err, "error generating random id for admin") } adm.AuthorityId = db.authorityID @@ -136,7 +148,7 @@ func (db *DB) CreateAdmin(ctx context.Context, adm *linkedca.Admin) error { CreatedAt: clock.Now(), } - return db.save(ctx, dba.ID, dba, nil, "admin", authorityAdminsTable) + return db.save(ctx, dba.ID, dba, nil, "admin", adminsTable) } // UpdateAdmin saves an updated admin to the database. @@ -149,7 +161,7 @@ func (db *DB) UpdateAdmin(ctx context.Context, adm *linkedca.Admin) error { nu := old.clone() nu.Type = adm.Type - return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) + return db.save(ctx, old.ID, nu, old, "admin", adminsTable) } // DeleteAdmin saves an updated admin to the database. @@ -162,5 +174,5 @@ func (db *DB) DeleteAdmin(ctx context.Context, id string) error { nu := old.clone() nu.DeletedAt = clock.Now() - return db.save(ctx, old.ID, nu, old, "admin", authorityAdminsTable) + return db.save(ctx, old.ID, nu, old, "admin", adminsTable) } diff --git a/authority/admin/db/nosql/admin_test.go b/authority/admin/db/nosql/admin_test.go new file mode 100644 index 00000000..092d72db --- /dev/null +++ b/authority/admin/db/nosql/admin_test.go @@ -0,0 +1,1121 @@ +package nosql + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/assert" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/db" + "github.com/smallstep/nosql" + "github.com/smallstep/nosql/database" + nosqldb "github.com/smallstep/nosql/database" + "go.step.sm/linkedca" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestDB_getDBAdminBytes(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "ok": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return []byte("foo"), nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db} + if b, err := db.getDBAdminBytes(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) { + assert.Equals(t, string(b), "foo") + } + } + }) + } +} + +func TestDB_getDBAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted": func(t *testing.T) test { + now := clock.Now() + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: now, + DeletedAt: now, + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "ok": func(t *testing.T) test { + now := clock.Now() + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: now, + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if dba, err := db.getDBAdmin(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dba.ID, adminID) + assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) + assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) + assert.Equals(t, dba.Subject, tc.dba.Subject) + assert.Equals(t, dba.Type, tc.dba.Type) + assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) + assert.Fatal(t, dba.DeletedAt.IsZero()) + } + } + }) + } +} + +func TestDB_unmarshalDBAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + in []byte + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dba := &dbAdmin{ + DeletedAt: time.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "fail/authority-mismatch-error": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: "foo", + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", adminID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + Subject: "max@smallstep.com", + ProvisionerID: "provID", + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if dba, err := db.unmarshalDBAdmin(tc.in, adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dba.ID, adminID) + assert.Equals(t, dba.AuthorityID, tc.dba.AuthorityID) + assert.Equals(t, dba.ProvisionerID, tc.dba.ProvisionerID) + assert.Equals(t, dba.Subject, tc.dba.Subject) + assert.Equals(t, dba.Type, tc.dba.Type) + assert.Equals(t, dba.CreatedAt, tc.dba.CreatedAt) + assert.Fatal(t, dba.DeletedAt.IsZero()) + } + } + }) + } +} + +func TestDB_unmarshalAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + in []byte + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dba := &dbAdmin{ + DeletedAt: time.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + Subject: "max@smallstep.com", + ProvisionerID: "provID", + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + in: data, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if adm, err := db.unmarshalAdmin(tc.in, adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, adm.Id, adminID) + assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) + assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) + assert.Equals(t, adm.Subject, tc.dba.Subject) + assert.Equals(t, adm.Type, tc.dba.Type) + assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) + assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) + } + } + }) + } +} + +func TestDB_GetAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dba *dbAdmin + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling admin adminID into dbAdmin"), + } + }, + "fail/deleted": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + DeletedAt: clock.Now(), + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + adminErr: admin.NewError(admin.ErrorDeletedType, "admin adminID is deleted"), + } + }, + "fail/authorityID-mismatch": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: "foo", + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "admin %s is not owned by authority %s", dba.ID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + b, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return b, nil + }, + }, + dba: dba, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if adm, err := db.GetAdmin(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, adm.Id, adminID) + assert.Equals(t, adm.AuthorityId, tc.dba.AuthorityID) + assert.Equals(t, adm.ProvisionerId, tc.dba.ProvisionerID) + assert.Equals(t, adm.Subject, tc.dba.Subject) + assert.Equals(t, adm.Type, tc.dba.Type) + assert.Equals(t, adm.CreatedAt, timestamppb.New(tc.dba.CreatedAt)) + assert.Equals(t, adm.DeletedAt, timestamppb.New(tc.dba.DeletedAt)) + } + } + }) + } +} + +func TestDB_DeleteAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/save-error": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, dba.Type) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + assert.True(t, _dba.DeletedAt.Before(time.Now())) + assert.True(t, _dba.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority admin: force"), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, dba.Type) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + assert.True(t, _dba.DeletedAt.Before(time.Now())) + assert.True(t, _dba.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.DeleteAdmin(context.Background(), adminID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_UpdateAdmin(t *testing.T) { + adminID := "adminID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + adm *linkedca.Admin + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + adm: &linkedca.Admin{Id: adminID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "admin adminID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + adm: &linkedca.Admin{Id: adminID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admin adminID: force"), + } + }, + "fail/save-error": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + + upd := dba.convert() + upd.Type = linkedca.Admin_ADMIN + + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + adm: upd, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority admin: force"), + } + }, + "ok": func(t *testing.T) test { + dba := &dbAdmin{ + ID: adminID, + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: clock.Now(), + } + + upd := dba.convert() + upd.Type = linkedca.Admin_ADMIN + + data, err := json.Marshal(dba) + assert.FatalError(t, err) + return test{ + adm: upd, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, string(key), adminID) + assert.Equals(t, string(old), string(data)) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.Equals(t, _dba.ID, dba.ID) + assert.Equals(t, _dba.AuthorityID, dba.AuthorityID) + assert.Equals(t, _dba.ProvisionerID, dba.ProvisionerID) + assert.Equals(t, _dba.Subject, dba.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + assert.Equals(t, _dba.CreatedAt, dba.CreatedAt) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.UpdateAdmin(context.Background(), tc.adm); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_CreateAdmin(t *testing.T) { + type test struct { + db nosql.DB + err error + adminErr *admin.Error + adm *linkedca.Admin + } + var tests = map[string]func(t *testing.T) test{ + "fail/save-error": func(t *testing.T) test { + adm := &linkedca.Admin{ + AuthorityId: admin.DefaultAuthorityID, + ProvisionerId: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_ADMIN, + } + + return test{ + adm: adm, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, old, nil) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key)) + assert.Equals(t, _dba.AuthorityID, adm.AuthorityId) + assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId) + assert.Equals(t, _dba.Subject, adm.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + + assert.True(t, _dba.CreatedAt.Before(time.Now())) + assert.True(t, _dba.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority admin: force"), + } + }, + "ok": func(t *testing.T) test { + adm := &linkedca.Admin{ + AuthorityId: admin.DefaultAuthorityID, + ProvisionerId: "provID", + Subject: "max@smallstep.com", + Type: linkedca.Admin_ADMIN, + } + + return test{ + adm: adm, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, adminsTable) + assert.Equals(t, old, nil) + + var _dba = new(dbAdmin) + assert.FatalError(t, json.Unmarshal(nu, _dba)) + + assert.True(t, len(_dba.ID) > 0 && _dba.ID == string(key)) + assert.Equals(t, _dba.AuthorityID, adm.AuthorityId) + assert.Equals(t, _dba.ProvisionerID, adm.ProvisionerId) + assert.Equals(t, _dba.Subject, adm.Subject) + assert.Equals(t, _dba.Type, linkedca.Admin_ADMIN) + + assert.True(t, _dba.CreatedAt.Before(time.Now())) + assert.True(t, _dba.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.CreateAdmin(context.Background(), tc.adm); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_GetAdmins(t *testing.T) { + now := clock.Now() + fooAdmin := &dbAdmin{ + ID: "foo", + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "foo@smallstep.com", + Type: linkedca.Admin_SUPER_ADMIN, + CreatedAt: now, + } + foob, err := json.Marshal(fooAdmin) + assert.FatalError(t, err) + + barAdmin := &dbAdmin{ + ID: "bar", + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "bar@smallstep.com", + Type: linkedca.Admin_ADMIN, + CreatedAt: now, + DeletedAt: now, + } + barb, err := json.Marshal(barAdmin) + assert.FatalError(t, err) + + bazAdmin := &dbAdmin{ + ID: "baz", + AuthorityID: "bazzer", + ProvisionerID: "provID", + Subject: "baz@smallstep.com", + Type: linkedca.Admin_ADMIN, + CreatedAt: now, + } + bazb, err := json.Marshal(bazAdmin) + assert.FatalError(t, err) + + zapAdmin := &dbAdmin{ + ID: "zap", + AuthorityID: admin.DefaultAuthorityID, + ProvisionerID: "provID", + Subject: "zap@smallstep.com", + Type: linkedca.Admin_ADMIN, + CreatedAt: now, + } + zapb, err := json.Marshal(zapAdmin) + assert.FatalError(t, err) + type test struct { + db nosql.DB + err error + adminErr *admin.Error + verify func(*testing.T, []*linkedca.Admin) + } + var tests = map[string]func(t *testing.T) test{ + "fail/db.List-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading admins: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: adminsTable, Key: []byte("foo"), Value: foob}, + {Bucket: adminsTable, Key: []byte("bar"), Value: barb}, + {Bucket: adminsTable, Key: []byte("zap"), Value: []byte("zap")}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + err: errors.New("error unmarshaling admin zap into dbAdmin"), + } + }, + "ok/none": func(t *testing.T) test { + ret := []*database.Entry{} + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, admins []*linkedca.Admin) { + assert.Equals(t, len(admins), 0) + }, + } + }, + "ok/only-invalid": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: adminsTable, Key: []byte("bar"), Value: barb}, + {Bucket: adminsTable, Key: []byte("baz"), Value: bazb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, admins []*linkedca.Admin) { + assert.Equals(t, len(admins), 0) + }, + } + }, + "ok": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: adminsTable, Key: []byte("foo"), Value: foob}, + {Bucket: adminsTable, Key: []byte("bar"), Value: barb}, + {Bucket: adminsTable, Key: []byte("baz"), Value: bazb}, + {Bucket: adminsTable, Key: []byte("zap"), Value: zapb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, adminsTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, admins []*linkedca.Admin) { + assert.Equals(t, len(admins), 2) + + assert.Equals(t, admins[0].Id, fooAdmin.ID) + assert.Equals(t, admins[0].AuthorityId, fooAdmin.AuthorityID) + assert.Equals(t, admins[0].ProvisionerId, fooAdmin.ProvisionerID) + assert.Equals(t, admins[0].Subject, fooAdmin.Subject) + assert.Equals(t, admins[0].Type, fooAdmin.Type) + assert.Equals(t, admins[0].CreatedAt, timestamppb.New(fooAdmin.CreatedAt)) + assert.Equals(t, admins[0].DeletedAt, timestamppb.New(fooAdmin.DeletedAt)) + + assert.Equals(t, admins[1].Id, zapAdmin.ID) + assert.Equals(t, admins[1].AuthorityId, zapAdmin.AuthorityID) + assert.Equals(t, admins[1].ProvisionerId, zapAdmin.ProvisionerID) + assert.Equals(t, admins[1].Subject, zapAdmin.Subject) + assert.Equals(t, admins[1].Type, zapAdmin.Type) + assert.Equals(t, admins[1].CreatedAt, timestamppb.New(zapAdmin.CreatedAt)) + assert.Equals(t, admins[1].DeletedAt, timestamppb.New(zapAdmin.DeletedAt)) + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if admins, err := db.GetAdmins(context.Background()); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + tc.verify(t, admins) + } + } + }) + } +} diff --git a/authority/mgmt/db/nosql/nosql.go b/authority/admin/db/nosql/nosql.go similarity index 81% rename from authority/mgmt/db/nosql/nosql.go rename to authority/admin/db/nosql/nosql.go index 47f79bde..2ce4297c 100644 --- a/authority/mgmt/db/nosql/nosql.go +++ b/authority/admin/db/nosql/nosql.go @@ -11,10 +11,8 @@ import ( ) var ( - authorityAdminsTable = []byte("authority_admins") - authorityConfigsTable = []byte("authority_configs") - authorityProvisionersTable = []byte("authority_provisioners") - authorityProvisionersNameIDIndexTable = []byte("authority_provisioners_name_id_index") + adminsTable = []byte("admins") + provisionersTable = []byte("provisioners") ) // DB is a struct that implements the AcmeDB interface. @@ -25,7 +23,7 @@ type DB struct { // New configures and returns a new Authority DB backend implemented using a nosql DB. func New(db nosqlDB.DB, authorityID string) (*DB, error) { - tables := [][]byte{authorityAdminsTable, authorityConfigsTable, authorityProvisionersTable, authorityProvisionersNameIDIndexTable} + tables := [][]byte{adminsTable, provisionersTable} for _, b := range tables { if err := db.CreateTable(b); err != nil { return nil, errors.Wrapf(err, "error creating table %s", @@ -71,8 +69,6 @@ func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface } } -var idLen = 32 - func randID() (val string, err error) { val, err = randutil.UUIDv4() if err != nil { diff --git a/authority/admin/db/nosql/provisioner.go b/authority/admin/db/nosql/provisioner.go new file mode 100644 index 00000000..71d9c8d6 --- /dev/null +++ b/authority/admin/db/nosql/provisioner.go @@ -0,0 +1,211 @@ +package nosql + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/nosql" + "go.step.sm/linkedca" + "google.golang.org/protobuf/types/known/timestamppb" +) + +// dbProvisioner is the database representation of a Provisioner type. +type dbProvisioner struct { + ID string `json:"id"` + AuthorityID string `json:"authorityID"` + Type linkedca.Provisioner_Type `json:"type"` + Name string `json:"name"` + Claims *linkedca.Claims `json:"claims"` + Details []byte `json:"details"` + X509Template *linkedca.Template `json:"x509Template"` + SSHTemplate *linkedca.Template `json:"sshTemplate"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt time.Time `json:"deletedAt"` +} + +func (dbp *dbProvisioner) clone() *dbProvisioner { + u := *dbp + return &u +} + +func (dbp *dbProvisioner) convert2linkedca() (*linkedca.Provisioner, error) { + details, err := admin.UnmarshalProvisionerDetails(dbp.Type, dbp.Details) + if err != nil { + return nil, err + } + + return &linkedca.Provisioner{ + Id: dbp.ID, + AuthorityId: dbp.AuthorityID, + Type: dbp.Type, + Name: dbp.Name, + Claims: dbp.Claims, + Details: details, + X509Template: dbp.X509Template, + SshTemplate: dbp.SSHTemplate, + CreatedAt: timestamppb.New(dbp.CreatedAt), + DeletedAt: timestamppb.New(dbp.DeletedAt), + }, nil +} + +func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { + data, err := db.db.Get(provisionersTable, []byte(id)) + if nosql.IsErrNotFound(err) { + return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id) + } else if err != nil { + return nil, errors.Wrapf(err, "error loading provisioner %s", id) + } + return data, nil +} + +func (db *DB) unmarshalDBProvisioner(data []byte, id string) (*dbProvisioner, error) { + var dbp = new(dbProvisioner) + if err := json.Unmarshal(data, dbp); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", id) + } + if !dbp.DeletedAt.IsZero() { + return nil, admin.NewError(admin.ErrorDeletedType, "provisioner %s is deleted", id) + } + if dbp.AuthorityID != db.authorityID { + return nil, admin.NewError(admin.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", id, db.authorityID) + } + return dbp, nil +} + +func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, error) { + data, err := db.getDBProvisionerBytes(ctx, id) + if err != nil { + return nil, err + } + dbp, err := db.unmarshalDBProvisioner(data, id) + if err != nil { + return nil, err + } + return dbp, nil +} + +func (db *DB) unmarshalProvisioner(data []byte, id string) (*linkedca.Provisioner, error) { + dbp, err := db.unmarshalDBProvisioner(data, id) + if err != nil { + return nil, err + } + + return dbp.convert2linkedca() +} + +// GetProvisioner retrieves and unmarshals a provisioner from the database. +func (db *DB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { + data, err := db.getDBProvisionerBytes(ctx, id) + if err != nil { + return nil, err + } + + prov, err := db.unmarshalProvisioner(data, id) + if err != nil { + return nil, err + } + return prov, nil +} + +// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners +// from the database. +func (db *DB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { + dbEntries, err := db.db.List(provisionersTable) + if err != nil { + return nil, errors.Wrap(err, "error loading provisioners") + } + var provs []*linkedca.Provisioner + for _, entry := range dbEntries { + prov, err := db.unmarshalProvisioner(entry.Value, string(entry.Key)) + if err != nil { + switch k := err.(type) { + case *admin.Error: + if k.IsType(admin.ErrorDeletedType) || k.IsType(admin.ErrorAuthorityMismatchType) { + continue + } else { + return nil, err + } + default: + return nil, err + } + } + if prov.AuthorityId != db.authorityID { + continue + } + provs = append(provs, prov) + } + return provs, nil +} + +// CreateProvisioner stores a new provisioner to the database. +func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + var err error + prov.Id, err = randID() + if err != nil { + return admin.WrapErrorISE(err, "error generating random id for provisioner") + } + + details, err := json.Marshal(prov.Details.GetData()) + if err != nil { + return admin.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name) + } + + dbp := &dbProvisioner{ + ID: prov.Id, + AuthorityID: db.authorityID, + Type: prov.Type, + Name: prov.Name, + Claims: prov.Claims, + Details: details, + X509Template: prov.X509Template, + SSHTemplate: prov.SshTemplate, + CreatedAt: clock.Now(), + } + + if err := db.save(ctx, prov.Id, dbp, nil, "provisioner", provisionersTable); err != nil { + return admin.WrapErrorISE(err, "error creating provisioner %s", prov.Name) + } + + return nil +} + +// UpdateProvisioner saves an updated provisioner to the database. +func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + old, err := db.getDBProvisioner(ctx, prov.Id) + if err != nil { + return err + } + + nu := old.clone() + + if old.Type != prov.Type { + return admin.NewError(admin.ErrorBadRequestType, "cannot update provisioner type") + } + nu.Name = prov.Name + nu.Claims = prov.Claims + nu.Details, err = json.Marshal(prov.Details.GetData()) + if err != nil { + return admin.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name) + } + nu.X509Template = prov.X509Template + nu.SSHTemplate = prov.SshTemplate + + return db.save(ctx, prov.Id, nu, old, "provisioner", provisionersTable) +} + +// DeleteProvisioner saves an updated admin to the database. +func (db *DB) DeleteProvisioner(ctx context.Context, id string) error { + old, err := db.getDBProvisioner(ctx, id) + if err != nil { + return err + } + + nu := old.clone() + nu.DeletedAt = clock.Now() + + return db.save(ctx, old.ID, nu, old, "provisioner", provisionersTable) +} diff --git a/authority/admin/db/nosql/provisioner_test.go b/authority/admin/db/nosql/provisioner_test.go new file mode 100644 index 00000000..95811f26 --- /dev/null +++ b/authority/admin/db/nosql/provisioner_test.go @@ -0,0 +1,1221 @@ +package nosql + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/smallstep/assert" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/db" + "github.com/smallstep/nosql" + "github.com/smallstep/nosql/database" + nosqldb "github.com/smallstep/nosql/database" + "go.step.sm/linkedca" +) + +func TestDB_getDBProvisionerBytes(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "ok": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return []byte("foo"), nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db} + if b, err := db.getDBProvisionerBytes(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, string(b), "foo") + } + } + }) + } +} + +func TestDB_getDBProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted": func(t *testing.T) test { + now := clock.Now() + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_JWK, + Name: "provName", + CreatedAt: now, + DeletedAt: now, + } + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner provID is deleted"), + } + }, + "ok": func(t *testing.T) test { + now := clock.Now() + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_JWK, + Name: "provName", + CreatedAt: now, + } + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if dbp, err := db.getDBProvisioner(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dbp.ID, provID) + assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) + assert.Equals(t, dbp.Type, tc.dbp.Type) + assert.Equals(t, dbp.Name, tc.dbp.Name) + assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) + assert.Fatal(t, dbp.DeletedAt.IsZero()) + } + } + }) + } +} + +func TestDB_unmarshalDBProvisioner(t *testing.T) { + provID := "provID" + type test struct { + in []byte + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dbp := &dbProvisioner{ + DeletedAt: clock.Now(), + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner %s is deleted", provID), + } + }, + "fail/authority-mismatch-error": func(t *testing.T) test { + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: "foo", + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", provID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dbp := &dbProvisioner{ + ID: provID, + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_JWK, + Name: "provName", + CreatedAt: clock.Now(), + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if dbp, err := db.unmarshalDBProvisioner(tc.in, provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, dbp.ID, provID) + assert.Equals(t, dbp.AuthorityID, tc.dbp.AuthorityID) + assert.Equals(t, dbp.Type, tc.dbp.Type) + assert.Equals(t, dbp.Name, tc.dbp.Name) + assert.Equals(t, dbp.Details, tc.dbp.Details) + assert.Equals(t, dbp.Claims, tc.dbp.Claims) + assert.Equals(t, dbp.X509Template, tc.dbp.X509Template) + assert.Equals(t, dbp.SSHTemplate, tc.dbp.SSHTemplate) + assert.Equals(t, dbp.CreatedAt, tc.dbp.CreatedAt) + assert.Fatal(t, dbp.DeletedAt.IsZero()) + } + } + }) + } +} + +func defaultDBP(t *testing.T) *dbProvisioner { + details := &linkedca.ProvisionerDetails_ACME{ + ACME: &linkedca.ACMEProvisioner{ + ForceCn: true, + }, + } + detailBytes, err := json.Marshal(details) + assert.FatalError(t, err) + + return &dbProvisioner{ + ID: "provID", + AuthorityID: admin.DefaultAuthorityID, + Type: linkedca.Provisioner_ACME, + Name: "provName", + Details: detailBytes, + Claims: &linkedca.Claims{ + DisableRenewal: true, + X509: &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Min: "5m", + Max: "12h", + Default: "6h", + }, + }, + Ssh: &linkedca.SSHClaims{ + Enabled: true, + UserDurations: &linkedca.Durations{ + Min: "5m", + Max: "12h", + Default: "6h", + }, + HostDurations: &linkedca.Durations{ + Min: "5m", + Max: "12h", + Default: "6h", + }, + }, + }, + X509Template: &linkedca.Template{ + Template: []byte("foo"), + Data: []byte("bar"), + }, + SSHTemplate: &linkedca.Template{ + Template: []byte("baz"), + Data: []byte("zap"), + }, + CreatedAt: clock.Now(), + } +} + +func TestDB_unmarshalProvisioner(t *testing.T) { + provID := "provID" + type test struct { + in []byte + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + in: []byte("foo"), + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted-error": func(t *testing.T) test { + dbp := &dbProvisioner{ + DeletedAt: time.Now(), + } + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner provID is deleted"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + in: data, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{authorityID: admin.DefaultAuthorityID} + if prov, err := db.unmarshalProvisioner(tc.in, provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, prov.Id, provID) + assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) + assert.Equals(t, prov.Type, tc.dbp.Type) + assert.Equals(t, prov.Name, tc.dbp.Name) + assert.Equals(t, prov.Claims, tc.dbp.Claims) + assert.Equals(t, prov.X509Template, tc.dbp.X509Template) + assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, tc.dbp.Details) + } + } + }) + } +} + +func TestDB_GetProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + dbp *dbProvisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return []byte("foo"), nil + }, + }, + err: errors.New("error unmarshaling provisioner provID into dbProvisioner"), + } + }, + "fail/deleted": func(t *testing.T) test { + dbp := defaultDBP(t) + dbp.DeletedAt = clock.Now() + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner provID is deleted"), + } + }, + "fail/authorityID-mismatch": func(t *testing.T) test { + dbp := defaultDBP(t) + dbp.AuthorityID = "foo" + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + adminErr: admin.NewError(admin.ErrorAuthorityMismatchType, + "provisioner %s is not owned by authority %s", dbp.ID, admin.DefaultAuthorityID), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + b, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return b, nil + }, + }, + dbp: dbp, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if prov, err := db.GetProvisioner(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + assert.Equals(t, prov.Id, provID) + assert.Equals(t, prov.AuthorityId, tc.dbp.AuthorityID) + assert.Equals(t, prov.Type, tc.dbp.Type) + assert.Equals(t, prov.Name, tc.dbp.Name) + assert.Equals(t, prov.Claims, tc.dbp.Claims) + assert.Equals(t, prov.X509Template, tc.dbp.X509Template) + assert.Equals(t, prov.SshTemplate, tc.dbp.SSHTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, tc.dbp.Details) + } + } + }) + } +} + +func TestDB_DeleteProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/save-error": func(t *testing.T) test { + dbp := defaultDBP(t) + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.Equals(t, _dbp.ID, provID) + assert.Equals(t, _dbp.AuthorityID, dbp.AuthorityID) + assert.Equals(t, _dbp.Type, dbp.Type) + assert.Equals(t, _dbp.Name, dbp.Name) + assert.Equals(t, _dbp.Claims, dbp.Claims) + assert.Equals(t, _dbp.X509Template, dbp.X509Template) + assert.Equals(t, _dbp.SSHTemplate, dbp.SSHTemplate) + assert.Equals(t, _dbp.CreatedAt, dbp.CreatedAt) + assert.Equals(t, _dbp.Details, dbp.Details) + + assert.True(t, _dbp.DeletedAt.Before(time.Now())) + assert.True(t, _dbp.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority provisioner: force"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.Equals(t, _dbp.ID, provID) + assert.Equals(t, _dbp.AuthorityID, dbp.AuthorityID) + assert.Equals(t, _dbp.Type, dbp.Type) + assert.Equals(t, _dbp.Name, dbp.Name) + assert.Equals(t, _dbp.Claims, dbp.Claims) + assert.Equals(t, _dbp.X509Template, dbp.X509Template) + assert.Equals(t, _dbp.SSHTemplate, dbp.SSHTemplate) + assert.Equals(t, _dbp.CreatedAt, dbp.CreatedAt) + assert.Equals(t, _dbp.Details, dbp.Details) + + assert.True(t, _dbp.DeletedAt.Before(time.Now())) + assert.True(t, _dbp.DeletedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.DeleteProvisioner(context.Background(), provID); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_GetProvisioners(t *testing.T) { + fooProv := defaultDBP(t) + fooProv.Name = "foo" + foob, err := json.Marshal(fooProv) + assert.FatalError(t, err) + + barProv := defaultDBP(t) + barProv.Name = "bar" + barProv.DeletedAt = clock.Now() + barb, err := json.Marshal(barProv) + assert.FatalError(t, err) + + bazProv := defaultDBP(t) + bazProv.Name = "baz" + bazProv.AuthorityID = "baz" + bazb, err := json.Marshal(bazProv) + assert.FatalError(t, err) + + zapProv := defaultDBP(t) + zapProv.Name = "zap" + zapb, err := json.Marshal(zapProv) + assert.FatalError(t, err) + + type test struct { + db nosql.DB + err error + adminErr *admin.Error + verify func(*testing.T, []*linkedca.Provisioner) + } + var tests = map[string]func(t *testing.T) test{ + "fail/db.List-error": func(t *testing.T) test { + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioners"), + } + }, + "fail/unmarshal-error": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: provisionersTable, Key: []byte("foo"), Value: foob}, + {Bucket: provisionersTable, Key: []byte("bar"), Value: barb}, + {Bucket: provisionersTable, Key: []byte("zap"), Value: []byte("zap")}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + err: errors.New("error unmarshaling provisioner zap into dbProvisioner"), + } + }, + "ok/none": func(t *testing.T) test { + ret := []*database.Entry{} + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, provs []*linkedca.Provisioner) { + assert.Equals(t, len(provs), 0) + }, + } + }, + "ok/only-invalid": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: provisionersTable, Key: []byte("bar"), Value: barb}, + {Bucket: provisionersTable, Key: []byte("baz"), Value: bazb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, provs []*linkedca.Provisioner) { + assert.Equals(t, len(provs), 0) + }, + } + }, + "ok": func(t *testing.T) test { + ret := []*database.Entry{ + {Bucket: provisionersTable, Key: []byte("foo"), Value: foob}, + {Bucket: provisionersTable, Key: []byte("bar"), Value: barb}, + {Bucket: provisionersTable, Key: []byte("baz"), Value: bazb}, + {Bucket: provisionersTable, Key: []byte("zap"), Value: zapb}, + } + return test{ + db: &db.MockNoSQLDB{ + MList: func(bucket []byte) ([]*database.Entry, error) { + assert.Equals(t, bucket, provisionersTable) + + return ret, nil + }, + }, + verify: func(t *testing.T, provs []*linkedca.Provisioner) { + assert.Equals(t, len(provs), 2) + + assert.Equals(t, provs[0].Id, fooProv.ID) + assert.Equals(t, provs[0].AuthorityId, fooProv.AuthorityID) + assert.Equals(t, provs[0].Type, fooProv.Type) + assert.Equals(t, provs[0].Name, fooProv.Name) + assert.Equals(t, provs[0].Claims, fooProv.Claims) + assert.Equals(t, provs[0].X509Template, fooProv.X509Template) + assert.Equals(t, provs[0].SshTemplate, fooProv.SSHTemplate) + + retDetailsBytes, err := json.Marshal(provs[0].Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, fooProv.Details) + + assert.Equals(t, provs[1].Id, zapProv.ID) + assert.Equals(t, provs[1].AuthorityId, zapProv.AuthorityID) + assert.Equals(t, provs[1].Type, zapProv.Type) + assert.Equals(t, provs[1].Name, zapProv.Name) + assert.Equals(t, provs[1].Claims, zapProv.Claims) + assert.Equals(t, provs[1].X509Template, zapProv.X509Template) + assert.Equals(t, provs[1].SshTemplate, zapProv.SSHTemplate) + + retDetailsBytes, err = json.Marshal(provs[1].Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, zapProv.Details) + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if provs, err := db.GetProvisioners(context.Background()); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } else { + if assert.Nil(t, tc.err) && assert.Nil(t, tc.adminErr) { + tc.verify(t, provs) + } + } + }) + } +} + +func TestDB_CreateProvisioner(t *testing.T) { + type test struct { + db nosql.DB + err error + adminErr *admin.Error + prov *linkedca.Provisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/save-error": func(t *testing.T) test { + dbp := defaultDBP(t) + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, old, nil) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + adminErr: admin.NewErrorISE("error creating provisioner provName: error saving authority provisioner: force"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, old, nil) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.CreateProvisioner(context.Background(), tc.prov); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} + +func TestDB_UpdateProvisioner(t *testing.T) { + provID := "provID" + type test struct { + db nosql.DB + err error + adminErr *admin.Error + prov *linkedca.Provisioner + } + var tests = map[string]func(t *testing.T) test{ + "fail/not-found": func(t *testing.T) test { + return test{ + prov: &linkedca.Provisioner{Id: provID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, nosqldb.ErrNotFound + }, + }, + adminErr: admin.NewError(admin.ErrorNotFoundType, "provisioner provID not found"), + } + }, + "fail/db.Get-error": func(t *testing.T) test { + return test{ + prov: &linkedca.Provisioner{Id: provID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return nil, errors.New("force") + }, + }, + err: errors.New("error loading provisioner provID: force"), + } + }, + "fail/update-deleted": func(t *testing.T) test { + dbp := defaultDBP(t) + dbp.DeletedAt = clock.Now() + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: &linkedca.Provisioner{Id: provID}, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + }, + adminErr: admin.NewError(admin.ErrorDeletedType, "provisioner %s is deleted", provID), + } + }, + "fail/update-type-error": func(t *testing.T) test { + dbp := defaultDBP(t) + + upd, err := dbp.convert2linkedca() + assert.FatalError(t, err) + upd.Type = linkedca.Provisioner_JWK + + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: upd, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + }, + adminErr: admin.NewError(admin.ErrorBadRequestType, "cannot update provisioner type"), + } + }, + "fail/save-error": func(t *testing.T) test { + dbp := defaultDBP(t) + + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nil, false, errors.New("force") + }, + }, + err: errors.New("error saving authority provisioner: force"), + } + }, + "ok": func(t *testing.T) test { + dbp := defaultDBP(t) + + prov, err := dbp.convert2linkedca() + assert.FatalError(t, err) + + prov.Name = "new-name" + prov.Claims = &linkedca.Claims{ + DisableRenewal: true, + X509: &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Min: "10m", + Max: "8h", + Default: "4h", + }, + }, + Ssh: &linkedca.SSHClaims{ + Enabled: true, + UserDurations: &linkedca.Durations{ + Min: "7m", + Max: "11h", + Default: "5h", + }, + HostDurations: &linkedca.Durations{ + Min: "4m", + Max: "24h", + Default: "24h", + }, + }, + } + prov.X509Template = &linkedca.Template{ + Template: []byte("x"), + Data: []byte("y"), + } + prov.SshTemplate = &linkedca.Template{ + Template: []byte("z"), + Data: []byte("w"), + } + prov.Details = &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_ACME{ + ACME: &linkedca.ACMEProvisioner{ + ForceCn: false, + }, + }, + } + + data, err := json.Marshal(dbp) + assert.FatalError(t, err) + return test{ + prov: prov, + db: &db.MockNoSQLDB{ + MGet: func(bucket, key []byte) ([]byte, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + + return data, nil + }, + MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) { + assert.Equals(t, bucket, provisionersTable) + assert.Equals(t, string(key), provID) + assert.Equals(t, string(old), string(data)) + + var _dbp = new(dbProvisioner) + assert.FatalError(t, json.Unmarshal(nu, _dbp)) + + assert.True(t, len(_dbp.ID) > 0 && _dbp.ID == string(key)) + assert.Equals(t, _dbp.AuthorityID, prov.AuthorityId) + assert.Equals(t, _dbp.Type, prov.Type) + assert.Equals(t, _dbp.Name, prov.Name) + assert.Equals(t, _dbp.Claims, prov.Claims) + assert.Equals(t, _dbp.X509Template, prov.X509Template) + assert.Equals(t, _dbp.SSHTemplate, prov.SshTemplate) + + retDetailsBytes, err := json.Marshal(prov.Details.GetData()) + assert.FatalError(t, err) + assert.Equals(t, retDetailsBytes, _dbp.Details) + + assert.True(t, _dbp.DeletedAt.IsZero()) + assert.True(t, _dbp.CreatedAt.Before(time.Now())) + assert.True(t, _dbp.CreatedAt.After(time.Now().Add(-time.Minute))) + + return nu, true, nil + }, + }, + } + }, + } + for name, run := range tests { + tc := run(t) + t.Run(name, func(t *testing.T) { + db := DB{db: tc.db, authorityID: admin.DefaultAuthorityID} + if err := db.UpdateProvisioner(context.Background(), tc.prov); err != nil { + switch k := err.(type) { + case *admin.Error: + if assert.NotNil(t, tc.adminErr) { + assert.Equals(t, k.Type, tc.adminErr.Type) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + assert.Equals(t, k.Status, tc.adminErr.Status) + assert.Equals(t, k.Err.Error(), tc.adminErr.Err.Error()) + assert.Equals(t, k.Detail, tc.adminErr.Detail) + } + default: + if assert.NotNil(t, tc.err) { + assert.HasPrefix(t, err.Error(), tc.err.Error()) + } + } + } + }) + } +} diff --git a/authority/mgmt/errors.go b/authority/admin/errors.go similarity index 88% rename from authority/mgmt/errors.go rename to authority/admin/errors.go index 63c0652e..81ae6d6f 100644 --- a/authority/mgmt/errors.go +++ b/authority/admin/errors.go @@ -1,4 +1,4 @@ -package mgmt +package admin import ( "encoding/json" @@ -25,6 +25,10 @@ const ( ErrorDeletedType // ErrorBadRequestType bad request. ErrorBadRequestType + // ErrorNotImplementedType not implemented. + ErrorNotImplementedType + // ErrorUnauthorizedType internal server error. + ErrorUnauthorizedType // ErrorServerInternalType internal server error. ErrorServerInternalType ) @@ -41,6 +45,10 @@ func (ap ProblemType) String() string { return "deleted" case ErrorBadRequestType: return "badRequest" + case ErrorNotImplementedType: + return "notImplemented" + case ErrorUnauthorizedType: + return "unauthorized" case ErrorServerInternalType: return "internalServerError" default: @@ -75,12 +83,22 @@ var ( ErrorDeletedType: { typ: ErrorDeletedType.String(), details: "resource is deleted", - status: 403, + status: http.StatusUnauthorized, + }, + ErrorNotImplementedType: { + typ: ErrorNotImplementedType.String(), + details: "not implemented", + status: http.StatusNotImplemented, }, ErrorBadRequestType: { typ: ErrorBadRequestType.String(), details: "bad request", - status: 400, + status: http.StatusBadRequest, + }, + ErrorUnauthorizedType: { + typ: ErrorUnauthorizedType.String(), + details: "unauthorized", + status: http.StatusUnauthorized, }, ErrorServerInternalType: errorServerInternalMetadata, } @@ -199,7 +217,6 @@ func WriteError(w http.ResponseWriter, err *Error) { } } - fmt.Printf("err = %+v\n", err) if err := json.NewEncoder(w).Encode(err); err != nil { log.Println(err) } diff --git a/authority/administrator/collection.go b/authority/administrator/collection.go new file mode 100644 index 00000000..47d3a35d --- /dev/null +++ b/authority/administrator/collection.go @@ -0,0 +1,243 @@ +package administrator + +import ( + "sort" + "sync" + + "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/linkedca" +) + +// DefaultAdminLimit is the default limit for listing provisioners. +const DefaultAdminLimit = 20 + +// DefaultAdminMax is the maximum limit for listing provisioners. +const DefaultAdminMax = 100 + +type adminSlice []*linkedca.Admin + +func (p adminSlice) Len() int { return len(p) } +func (p adminSlice) Less(i, j int) bool { return p[i].Id < p[j].Id } +func (p adminSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +// Collection is a memory map of admins. +type Collection struct { + byID *sync.Map + bySubProv *sync.Map + byProv *sync.Map + sorted adminSlice + provisioners *provisioner.Collection + superCount int + superCountByProvisioner map[string]int +} + +// NewCollection initializes a collection of provisioners. The given list of +// audiences are the audiences used by the JWT provisioner. +func NewCollection(provisioners *provisioner.Collection) *Collection { + return &Collection{ + byID: new(sync.Map), + byProv: new(sync.Map), + bySubProv: new(sync.Map), + superCountByProvisioner: map[string]int{}, + provisioners: provisioners, + } +} + +// LoadByID a admin by the ID. +func (c *Collection) LoadByID(id string) (*linkedca.Admin, bool) { + return loadAdmin(c.byID, id) +} + +type subProv struct { + subject string + provisioner string +} + +func newSubProv(subject, provisioner string) subProv { + return subProv{subject, provisioner} +} + +// LoadBySubProv a admin by the subject and provisioner name. +func (c *Collection) LoadBySubProv(sub, provName string) (*linkedca.Admin, bool) { + return loadAdmin(c.bySubProv, newSubProv(sub, provName)) +} + +// LoadByProvisioner a admin by the subject and provisioner name. +func (c *Collection) LoadByProvisioner(provName string) ([]*linkedca.Admin, bool) { + val, ok := c.byProv.Load(provName) + if !ok { + return nil, false + } + admins, ok := val.([]*linkedca.Admin) + if !ok { + return nil, false + } + return admins, true +} + +// Store adds an admin to the collection and enforces the uniqueness of +// admin IDs and amdin subject <-> provisioner name combos. +func (c *Collection) Store(adm *linkedca.Admin, prov provisioner.Interface) error { + // Input validation. + if adm.ProvisionerId != prov.GetID() { + return admin.NewErrorISE("admin.provisionerId does not match provisioner argument") + } + + // Store admin always in byID. ID must be unique. + if _, loaded := c.byID.LoadOrStore(adm.Id, adm); loaded { + return errors.New("cannot add multiple admins with the same id") + } + + provName := prov.GetName() + // Store admin always in bySubProv. Subject <-> ProvisionerName must be unique. + if _, loaded := c.bySubProv.LoadOrStore(newSubProv(adm.Subject, provName), adm); loaded { + c.byID.Delete(adm.Id) + return errors.New("cannot add multiple admins with the same subject and provisioner") + } + + var isSuper = (adm.Type == linkedca.Admin_SUPER_ADMIN) + if admins, ok := c.LoadByProvisioner(provName); ok { + c.byProv.Store(provName, append(admins, adm)) + if isSuper { + c.superCountByProvisioner[provName]++ + } + } else { + c.byProv.Store(provName, []*linkedca.Admin{adm}) + if isSuper { + c.superCountByProvisioner[provName] = 1 + } + } + if isSuper { + c.superCount++ + } + + c.sorted = append(c.sorted, adm) + sort.Sort(c.sorted) + + return nil +} + +// Remove deletes an admin from all associated collections and lists. +func (c *Collection) Remove(id string) error { + adm, ok := c.LoadByID(id) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, "admin %s not found", id) + } + if adm.Type == linkedca.Admin_SUPER_ADMIN && c.SuperCount() == 1 { + return admin.NewError(admin.ErrorBadRequestType, "cannot remove the last super admin") + } + prov, ok := c.provisioners.Load(adm.ProvisionerId) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, + "provisioner %s for admin %s not found", adm.ProvisionerId, id) + } + provName := prov.GetName() + adminsByProv, ok := c.LoadByProvisioner(provName) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, + "admins not found for provisioner %s", provName) + } + + // Find index in sorted list. + sortedIndex := sort.Search(c.sorted.Len(), func(i int) bool { return c.sorted[i].Id >= adm.Id }) + if c.sorted[sortedIndex].Id != adm.Id { + return admin.NewError(admin.ErrorNotFoundType, + "admin %s not found in sorted list", adm.Id) + } + + var found bool + for i, a := range adminsByProv { + if a.Id == adm.Id { + // Remove admin from list. https://stackoverflow.com/questions/37334119/how-to-delete-an-element-from-a-slice-in-golang + // Order does not matter. + adminsByProv[i] = adminsByProv[len(adminsByProv)-1] + c.byProv.Store(provName, adminsByProv[:len(adminsByProv)-1]) + found = true + } + } + if !found { + return admin.NewError(admin.ErrorNotFoundType, + "admin %s not found in adminsByProvisioner list", adm.Id) + } + + // Remove index in sorted list + copy(c.sorted[sortedIndex:], c.sorted[sortedIndex+1:]) // Shift a[i+1:] left one index. + c.sorted[len(c.sorted)-1] = nil // Erase last element (write zero value). + c.sorted = c.sorted[:len(c.sorted)-1] // Truncate slice. + + c.byID.Delete(adm.Id) + c.bySubProv.Delete(newSubProv(adm.Subject, provName)) + + if adm.Type == linkedca.Admin_SUPER_ADMIN { + c.superCount-- + c.superCountByProvisioner[provName]-- + } + return nil +} + +// Update updates the given admin in all related lists and collections. +func (c *Collection) Update(id string, nu *linkedca.Admin) (*linkedca.Admin, error) { + adm, ok := c.LoadByID(id) + if !ok { + return nil, admin.NewError(admin.ErrorNotFoundType, "admin %s not found", adm.Id) + } + if adm.Type == nu.Type { + return nil, admin.NewError(admin.ErrorBadRequestType, "admin %s already has type %s", id, adm.Type) + } + if adm.Type == linkedca.Admin_SUPER_ADMIN && c.SuperCount() == 1 { + return nil, admin.NewError(admin.ErrorBadRequestType, "cannot change role of last super admin") + } + + adm.Type = nu.Type + return adm, nil +} + +// SuperCount returns the total number of admins. +func (c *Collection) SuperCount() int { + return c.superCount +} + +// SuperCountByProvisioner returns the total number of admins. +func (c *Collection) SuperCountByProvisioner(provName string) int { + if cnt, ok := c.superCountByProvisioner[provName]; ok { + return cnt + } + return 0 +} + +// Find implements pagination on a list of sorted admins. +func (c *Collection) Find(cursor string, limit int) ([]*linkedca.Admin, string) { + switch { + case limit <= 0: + limit = DefaultAdminLimit + case limit > DefaultAdminMax: + limit = DefaultAdminMax + } + + n := c.sorted.Len() + i := sort.Search(n, func(i int) bool { return c.sorted[i].Id >= cursor }) + + slice := []*linkedca.Admin{} + for ; i < n && len(slice) < limit; i++ { + slice = append(slice, c.sorted[i]) + } + + if i < n { + return slice, c.sorted[i].Id + } + return slice, "" +} + +func loadAdmin(m *sync.Map, key interface{}) (*linkedca.Admin, bool) { + val, ok := m.Load(key) + if !ok { + return nil, false + } + adm, ok := val.(*linkedca.Admin) + if !ok { + return nil, false + } + return adm, true +} diff --git a/authority/admins.go b/authority/admins.go new file mode 100644 index 00000000..dcaf9b49 --- /dev/null +++ b/authority/admins.go @@ -0,0 +1,97 @@ +package authority + +import ( + "context" + + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/provisioner" + "go.step.sm/linkedca" +) + +// LoadAdminByID returns an *linkedca.Admin with the given ID. +func (a *Authority) LoadAdminByID(id string) (*linkedca.Admin, bool) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + return a.admins.LoadByID(id) +} + +// LoadAdminBySubProv returns an *linkedca.Admin with the given ID. +func (a *Authority) LoadAdminBySubProv(subject, provisioner string) (*linkedca.Admin, bool) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + return a.admins.LoadBySubProv(subject, provisioner) +} + +// GetAdmins returns a map listing each provisioner and the JWK Key Set +// with their public keys. +func (a *Authority) GetAdmins(cursor string, limit int) ([]*linkedca.Admin, string, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + admins, nextCursor := a.admins.Find(cursor, limit) + return admins, nextCursor, nil +} + +// StoreAdmin stores an *linkedca.Admin to the authority. +func (a *Authority) StoreAdmin(ctx context.Context, adm *linkedca.Admin, prov provisioner.Interface) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + if adm.ProvisionerId != prov.GetID() { + return admin.NewErrorISE("admin.provisionerId does not match provisioner argument") + } + + if _, ok := a.admins.LoadBySubProv(adm.Subject, prov.GetName()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "admin with subject %s and provisioner %s already exists", adm.Subject, prov.GetName()) + } + // Store to database -- this will set the ID. + if err := a.adminDB.CreateAdmin(ctx, adm); err != nil { + return admin.WrapErrorISE(err, "error creating admin") + } + if err := a.admins.Store(adm, prov); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed admin store") + } + return admin.WrapErrorISE(err, "error storing admin in authority cache") + } + return nil +} + +// UpdateAdmin stores an *linkedca.Admin to the authority. +func (a *Authority) UpdateAdmin(ctx context.Context, id string, nu *linkedca.Admin) (*linkedca.Admin, error) { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + adm, err := a.admins.Update(id, nu) + if err != nil { + return nil, admin.WrapErrorISE(err, "error updating cached admin %s", id) + } + if err := a.adminDB.UpdateAdmin(ctx, adm); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return nil, admin.WrapErrorISE(err, "error reloading admin resources on failed admin update") + } + return nil, admin.WrapErrorISE(err, "error updating admin %s", id) + } + return adm, nil +} + +// RemoveAdmin removes an *linkedca.Admin from the authority. +func (a *Authority) RemoveAdmin(ctx context.Context, id string) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + return a.removeAdmin(ctx, id) +} + +// removeAdmin helper that assumes lock. +func (a *Authority) removeAdmin(ctx context.Context, id string) error { + if err := a.admins.Remove(id); err != nil { + return admin.WrapErrorISE(err, "error removing admin %s from authority cache", id) + } + if err := a.adminDB.DeleteAdmin(ctx, id); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed admin remove") + } + return admin.WrapErrorISE(err, "error deleting admin %s", id) + } + return nil +} diff --git a/authority/authority.go b/authority/authority.go index b5924061..eec0c3e0 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -11,14 +11,14 @@ import ( "time" "github.com/smallstep/certificates/cas" - "github.com/smallstep/certificates/linkedca" "github.com/smallstep/certificates/scep" + "go.step.sm/linkedca" "github.com/pkg/errors" "github.com/smallstep/certificates/authority/admin" + adminDBNosql "github.com/smallstep/certificates/authority/admin/db/nosql" + "github.com/smallstep/certificates/authority/administrator" "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" - authMgmtNosql "github.com/smallstep/certificates/authority/mgmt/db/nosql" "github.com/smallstep/certificates/authority/provisioner" casapi "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" @@ -34,16 +34,17 @@ import ( // Authority implements the Certificate Authority internal interface. type Authority struct { config *config.Config - adminDB mgmt.DB keyManager kms.KeyManager provisioners *provisioner.Collection - admins *admin.Collection + admins *administrator.Collection db db.AuthDB + adminDB admin.DB templates *templates.Templates // X509 CA x509CAService cas.CertificateAuthorityService rootX509Certs []*x509.Certificate + rootX509CertPool *x509.CertPool federatedX509Certs []*x509.Certificate certificates *sync.Map @@ -67,6 +68,8 @@ type Authority struct { sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error) sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error) getIdentityFunc provisioner.GetIdentityFunc + + adminMutex sync.RWMutex } // New creates and initiates a new Authority type. @@ -134,62 +137,62 @@ func NewEmbedded(opts ...Option) (*Authority, error) { return a, nil } -// ReloadAuthConfig reloads dynamic fields of the AuthConfig. -func (a *Authority) ReloadAuthConfig(ctx context.Context) error { - provs, err := a.adminDB.GetProvisioners(ctx) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } - a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) - if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates") - } - a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(ctx) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") +// reloadAdminResources reloads admins and provisioners from the DB. +func (a *Authority) reloadAdminResources(ctx context.Context) error { + var ( + provList provisioner.List + adminList []*linkedca.Admin + ) + if a.config.AuthorityConfig.EnableAdmin { + provs, err := a.adminDB.GetProvisioners(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error getting provisioners to initialize authority") + } + provList, err = provisionerListToCertificates(provs) + if err != nil { + return admin.WrapErrorISE(err, "error converting provisioner list to certificates") + } + adminList, err = a.adminDB.GetAdmins(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error getting admins to initialize authority") + } + } else { + provList = a.config.AuthorityConfig.Provisioners + adminList = a.config.AuthorityConfig.Admins } - // Merge global and configuration claims - claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) + provisionerConfig, err := a.generateProvisionerConfig(ctx) if err != nil { - return err - } - // TODO: should we also be combining the ssh federated roots here? - // If we rotate ssh roots keys, sshpop provisioner will lose ability to - // validate old SSH certificates, unless they are added as federated certs. - sshKeys, err := a.GetSSHRoots(ctx) - if err != nil { - return err - } - // Initialize provisioners - audiences := a.config.GetAudiences() - a.provisioners = provisioner.NewCollection(audiences) - config := provisioner.Config{ - Claims: claimer.Claims(), - Audiences: audiences, - DB: a.db, - SSHKeys: &provisioner.SSHKeys{ - UserKeys: sshKeys.UserKeys, - HostKeys: sshKeys.HostKeys, - }, - GetIdentityFunc: a.getIdentityFunc, + return admin.WrapErrorISE(err, "error generating provisioner config") } - // Store all the provisioners - for _, p := range a.config.AuthorityConfig.Provisioners { - if err := p.Init(config); err != nil { + + // Create provisioner collection. + provClxn := provisioner.NewCollection(provisionerConfig.Audiences) + for _, p := range provList { + if err := p.Init(*provisionerConfig); err != nil { return err } - if err := a.provisioners.Store(p); err != nil { + if err := provClxn.Store(p); err != nil { return err } } - // Store all the admins - a.admins = admin.NewCollection(a.provisioners) - for _, adm := range a.config.AuthorityConfig.Admins { - if err := a.admins.Store(adm); err != nil { + // Create admin collection. + adminClxn := administrator.NewCollection(provClxn) + for _, adm := range adminList { + p, ok := provClxn.Load(adm.ProvisionerId) + if !ok { + return admin.NewErrorISE("provisioner %s not found when loading admin %s", + adm.ProvisionerId, adm.Id) + } + if err := adminClxn.Store(adm, p); err != nil { return err } } + + a.config.AuthorityConfig.Provisioners = provList + a.provisioners = provClxn + a.config.AuthorityConfig.Admins = adminList + a.admins = adminClxn return nil } @@ -210,56 +213,6 @@ func (a *Authority) init() error { } } - if len(a.config.AuthorityConfig.Provisioners) == 0 { - // Initialize step-ca Admin Database if it's not already initialized using - // WithAdminDB. - if a.adminDB == nil { - // Check if AuthConfig already exists - a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID) - if err != nil { - return err - } - } - - provs, err := a.adminDB.GetProvisioners(context.Background()) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } - if len(provs) == 0 { - // Create First Provisioner - prov, err := mgmt.CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) - if err != nil { - return mgmt.WrapErrorISE(err, "error creating first provisioner") - } - certProv, err := provisionerToCertificates(prov) - if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner to certificates type") - } - a.config.AuthorityConfig.Provisioners = []provisioner.Interface{certProv} - - // Create First Admin - adm := &linkedca.Admin{ - ProvisionerId: prov.Id, - Subject: "step", - Type: linkedca.Admin_SUPER_ADMIN, - } - if err := a.adminDB.CreateAdmin(context.Background(), adm); err != nil { - // TODO should we try to clean up? - return mgmt.WrapErrorISE(err, "error creating first admin") - } - a.config.AuthorityConfig.Admins = []*linkedca.Admin{adm} - } else { - a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs) - if err != nil { - return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates type") - } - a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(context.Background()) - if err != nil { - return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority") - } - } - } - // Initialize key manager if it has not been set in the options. if a.keyManager == nil { var options kmsapi.Options @@ -329,6 +282,11 @@ func (a *Authority) init() error { a.certificates.Store(hex.EncodeToString(sum[:]), crt) } + a.rootX509CertPool = x509.NewCertPool() + for _, cert := range a.rootX509Certs { + a.rootX509CertPool.AddCert(cert) + } + // Read federated certificates and store them in the certificates map. if len(a.federatedX509Certs) == 0 { a.federatedX509Certs = make([]*x509.Certificate, len(a.config.FederatedRoots)) @@ -430,32 +388,6 @@ func (a *Authority) init() error { tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...) } - // Merge global and configuration claims - claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) - if err != nil { - return err - } - // TODO: should we also be combining the ssh federated roots here? - // If we rotate ssh roots keys, sshpop provisioner will lose ability to - // validate old SSH certificates, unless they are added as federated certs. - sshKeys, err := a.GetSSHRoots(context.Background()) - if err != nil { - return err - } - // Initialize provisioners - audiences := a.config.GetAudiences() - a.provisioners = provisioner.NewCollection(audiences) - config := provisioner.Config{ - Claims: claimer.Claims(), - Audiences: audiences, - DB: a.db, - SSHKeys: &provisioner.SSHKeys{ - UserKeys: sshKeys.UserKeys, - HostKeys: sshKeys.HostKeys, - }, - GetIdentityFunc: a.getIdentityFunc, - } - // Check if a KMS with decryption capability is required and available if a.requiresDecrypter() { if _, ok := a.keyManager.(kmsapi.Decrypter); !ok { @@ -499,23 +431,44 @@ func (a *Authority) init() error { // TODO: mimick the x509CAService GetCertificateAuthority here too? } - // Store all the provisioners - for _, p := range a.config.AuthorityConfig.Provisioners { - if err := p.Init(config); err != nil { - return err + if a.config.AuthorityConfig.EnableAdmin { + // Initialize step-ca Admin Database if it's not already initialized using + // WithAdminDB. + if a.adminDB == nil { + // Check if AuthConfig already exists + a.adminDB, err = adminDBNosql.New(a.db.(nosql.DB), admin.DefaultAuthorityID) + if err != nil { + return err + } } - if err := a.provisioners.Store(p); err != nil { - return err + + provs, err := a.adminDB.GetProvisioners(context.Background()) + if err != nil { + return admin.WrapErrorISE(err, "error loading provisioners to initialize authority") } - } - // Store all the admins - a.admins = admin.NewCollection(a.provisioners) - for _, adm := range a.config.AuthorityConfig.Admins { - if err := a.admins.Store(adm); err != nil { - return err + if len(provs) == 0 { + // Create First Provisioner + prov, err := CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password) + if err != nil { + return admin.WrapErrorISE(err, "error creating first provisioner") + } + + // Create first admin + if err := a.adminDB.CreateAdmin(context.Background(), &linkedca.Admin{ + ProvisionerId: prov.Id, + Subject: "step", + Type: linkedca.Admin_SUPER_ADMIN, + }); err != nil { + return admin.WrapErrorISE(err, "error creating first admin") + } } } + // Load Provisioners and Admins + if err := a.reloadAdminResources(context.Background()); err != nil { + return err + } + // Configure templates, currently only ssh templates are supported. if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil { a.templates = a.config.Templates @@ -543,21 +496,11 @@ func (a *Authority) GetDatabase() db.AuthDB { return a.db } -// GetAdminDatabase returns the mgmt database, if one exists. -func (a *Authority) GetAdminDatabase() mgmt.DB { +// GetAdminDatabase returns the admin database, if one exists. +func (a *Authority) GetAdminDatabase() admin.DB { return a.adminDB } -// GetAdminCollection returns the admin collection. -func (a *Authority) GetAdminCollection() *admin.Collection { - return a.admins -} - -// GetProvisionerCollection returns the admin collection. -func (a *Authority) GetProvisionerCollection() *provisioner.Collection { - return a.provisioners -} - // Shutdown safely shuts down any clients, databases, etc. held by the Authority. func (a *Authority) Shutdown() error { if err := a.keyManager.Close(); err != nil { diff --git a/authority/authority_test.go b/authority/authority_test.go index 618e7939..7604ec6b 100644 --- a/authority/authority_test.go +++ b/authority/authority_test.go @@ -454,8 +454,6 @@ func TestAuthority_GetSCEPService(t *testing.T) { // getIdentityFunc: tt.fields.getIdentityFunc, // } a, err := New(tt.fields.config) - fmt.Println(err) - fmt.Println(a) if (err != nil) != tt.wantErr { t.Errorf("Authority.New(), error = %v, wantErr %v", err, tt.wantErr) return diff --git a/authority/authorize.go b/authority/authorize.go index f84bd6f5..8d1f878a 100644 --- a/authority/authorize.go +++ b/authority/authorize.go @@ -7,10 +7,13 @@ import ( "encoding/hex" "net/http" "strings" + "time" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "go.step.sm/crypto/jose" + "go.step.sm/linkedca" "golang.org/x/crypto/ssh" ) @@ -73,25 +76,121 @@ func (a *Authority) authorizeToken(ctx context.Context, token string) (provision // Store the token to protect against reuse unless it's skipped. // If we cannot get a token id from the provisioner, just hash the token. if !SkipTokenReuseFromContext(ctx) { - if reuseKey, err := p.GetTokenID(token); err == nil { - if reuseKey == "" { - sum := sha256.Sum256([]byte(token)) - reuseKey = strings.ToLower(hex.EncodeToString(sum[:])) - } - ok, err := a.db.UseToken(reuseKey, token) - if err != nil { - return nil, errs.Wrap(http.StatusInternalServerError, err, - "authority.authorizeToken: failed when attempting to store token") - } - if !ok { - return nil, errs.Unauthorized("authority.authorizeToken: token already used") - } + if err = a.UseToken(token, p); err != nil { + return nil, err } } return p, nil } +// AuthorizeAdminToken authorize an Admin token. +func (a *Authority) AuthorizeAdminToken(r *http.Request, token string) (*linkedca.Admin, error) { + jwt, err := jose.ParseSigned(token) + if err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error parsing x5c token") + } + + verifiedChains, err := jwt.Headers[0].Certificates(x509.VerifyOptions{ + Roots: a.rootX509CertPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }) + if err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, + "adminHandler.authorizeToken; error verifying x5c certificate chain in token") + } + leaf := verifiedChains[0][0] + + if leaf.KeyUsage&x509.KeyUsageDigitalSignature == 0 { + return nil, admin.NewError(admin.ErrorUnauthorizedType, "adminHandler.authorizeToken; certificate used to sign x5c token cannot be used for digital signature") + } + + // Using the leaf certificates key to validate the claims accomplishes two + // things: + // 1. Asserts that the private key used to sign the token corresponds + // to the public certificate in the `x5c` header of the token. + // 2. Asserts that the claims are valid - have not been tampered with. + var claims jose.Claims + if err = jwt.Claims(leaf.PublicKey, &claims); err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error parsing x5c claims") + } + + prov, err := a.LoadProvisionerByCertificate(leaf) + if err != nil { + return nil, err + } + + // Check that the token has not been used. + if err = a.UseToken(token, prov); err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "adminHandler.authorizeToken; error with reuse token") + } + + // According to "rfc7519 JSON Web Token" acceptable skew should be no + // more than a few minutes. + if err = claims.ValidateWithLeeway(jose.Expected{ + Issuer: prov.GetName(), + Time: time.Now().UTC(), + }, time.Minute); err != nil { + return nil, admin.WrapError(admin.ErrorUnauthorizedType, err, "x5c.authorizeToken; invalid x5c claims") + } + + // validate audience: path matches the current path + if r.URL.Path != claims.Audience[0] { + return nil, admin.NewError(admin.ErrorUnauthorizedType, + "x5c.authorizeToken; x5c token has invalid audience "+ + "claim (aud); expected %s, but got %s", r.URL.Path, claims.Audience) + } + + if claims.Subject == "" { + return nil, admin.NewError(admin.ErrorUnauthorizedType, + "x5c.authorizeToken; x5c token subject cannot be empty") + } + + var ( + ok bool + adm *linkedca.Admin + ) + adminFound := false + adminSANs := append([]string{leaf.Subject.CommonName}, leaf.DNSNames...) + adminSANs = append(adminSANs, leaf.EmailAddresses...) + for _, san := range adminSANs { + if adm, ok = a.LoadAdminBySubProv(san, claims.Issuer); ok { + adminFound = true + break + } + } + if !adminFound { + return nil, admin.NewError(admin.ErrorUnauthorizedType, + "adminHandler.authorizeToken; unable to load admin with subject(s) %s and provisioner '%s'", + adminSANs, claims.Issuer) + } + + if strings.HasPrefix(r.URL.Path, "/admin/admins") && (r.Method != "GET") && adm.Type != linkedca.Admin_SUPER_ADMIN { + return nil, admin.NewError(admin.ErrorUnauthorizedType, "must have super admin access to make this request") + } + + return adm, nil +} + +// UseToken stores the token to protect against reuse. +func (a *Authority) UseToken(token string, prov provisioner.Interface) error { + if reuseKey, err := prov.GetTokenID(token); err == nil { + if reuseKey == "" { + sum := sha256.Sum256([]byte(token)) + reuseKey = strings.ToLower(hex.EncodeToString(sum[:])) + } + ok, err := a.db.UseToken(reuseKey, token) + if err != nil { + return errs.Wrap(http.StatusInternalServerError, err, + "authority.authorizeToken: failed when attempting to store token") + } + if !ok { + return errs.Unauthorized("authority.authorizeToken: token already used") + } + } + return nil +} + // Authorize grabs the method from the context and authorizes the request by // validating the one-time-token. func (a *Authority) Authorize(ctx context.Context, token string) ([]provisioner.SignOption, error) { diff --git a/authority/authorize_test.go b/authority/authorize_test.go index f20e2976..f308ec28 100644 --- a/authority/authorize_test.go +++ b/authority/authorize_test.go @@ -822,7 +822,7 @@ func TestAuthority_authorizeRenew(t *testing.T) { return &authorizeTest{ auth: a, cert: renewDisabledCrt, - err: errors.New("authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner renew_disabled:IMi94WBNI6gP5cNHXlZYNUzvMjGdHyBRmFoo-lCEaqk"), + err: errors.New("authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner 'renew_disabled'"), code: http.StatusUnauthorized, } }, diff --git a/authority/config.go b/authority/config.go index 79bdc7ff..744ca5e7 100644 --- a/authority/config.go +++ b/authority/config.go @@ -5,17 +5,26 @@ import "github.com/smallstep/certificates/authority/config" // Config is an alias to support older APIs. type Config = config.Config +// LoadConfiguration is an alias to support older APIs. +var LoadConfiguration = config.LoadConfiguration + // AuthConfig is an alias to support older APIs. type AuthConfig = config.AuthConfig +// TLS + // ASN1DN is an alias to support older APIs. type ASN1DN = config.ASN1DN -// TLS +// DefaultTLSOptions is an alias to support older APIs. +var DefaultTLSOptions = config.DefaultTLSOptions // TLSOptions is an alias to support older APIs. type TLSOptions = config.TLSOptions +// CipherSuites is an alias to support older APIs. +type CipherSuites = config.CipherSuites + // SSH // SSHConfig is an alias to support older APIs. diff --git a/authority/config/config.go b/authority/config/config.go index 55041142..9ad1ff5f 100644 --- a/authority/config/config.go +++ b/authority/config/config.go @@ -12,8 +12,8 @@ import ( cas "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" kms "github.com/smallstep/certificates/kms/apiv1" - "github.com/smallstep/certificates/linkedca" "github.com/smallstep/certificates/templates" + "go.step.sm/linkedca" ) const ( @@ -21,18 +21,6 @@ const ( ) var ( - // DefaultTLSOptions represents the default TLS version as well as the cipher - // suites used in the TLS certificates. - DefaultTLSOptions = TLSOptions{ - CipherSuites: CipherSuites{ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - }, - MinVersion: 1.2, - MaxVersion: 1.2, - Renegotiation: false, - } // DefaultBackdate length of time to backdate certificates to avoid // clock skew validation issues. DefaultBackdate = time.Minute @@ -41,7 +29,7 @@ var ( // DefaultEnableSSHCA enable SSH CA features per provisioner or globally // for all provisioners. DefaultEnableSSHCA = false - // GlobalProvisionerClaims default claims for the Authority. Can be overriden + // GlobalProvisionerClaims default claims for the Authority. Can be overridden // by provisioner specific claims. GlobalProvisionerClaims = provisioner.Claims{ MinTLSDur: &provisioner.Duration{Duration: 5 * time.Minute}, // TLS certs @@ -65,6 +53,7 @@ type Config struct { IntermediateCert string `json:"crt"` IntermediateKey string `json:"key"` Address string `json:"address"` + InsecureAddress string `json:"insecureAddress"` DNSNames []string `json:"dnsNames"` KMS *kms.Options `json:"kms,omitempty"` SSH *SSHConfig `json:"ssh,omitempty"` @@ -80,13 +69,13 @@ type Config struct { // ASN1DN contains ASN1.DN attributes that are used in Subject and Issuer // x509 Certificate blocks. type ASN1DN struct { - Country string `json:"country,omitempty" step:"country"` - Organization string `json:"organization,omitempty" step:"organization"` - OrganizationalUnit string `json:"organizationalUnit,omitempty" step:"organizationalUnit"` - Locality string `json:"locality,omitempty" step:"locality"` - Province string `json:"province,omitempty" step:"province"` - StreetAddress string `json:"streetAddress,omitempty" step:"streetAddress"` - CommonName string `json:"commonName,omitempty" step:"commonName"` + Country string `json:"country,omitempty"` + Organization string `json:"organization,omitempty"` + OrganizationalUnit string `json:"organizationalUnit,omitempty"` + Locality string `json:"locality,omitempty"` + Province string `json:"province,omitempty"` + StreetAddress string `json:"streetAddress,omitempty"` + CommonName string `json:"commonName,omitempty"` } // AuthConfig represents the configuration options for the authority. An @@ -101,6 +90,7 @@ type AuthConfig struct { Claims *provisioner.Claims `json:"claims,omitempty"` DisableIssuedAtCheck bool `json:"disableIssuedAtCheck,omitempty"` Backdate *provisioner.Duration `json:"backdate,omitempty"` + EnableAdmin bool `json:"enableAdmin,omitempty"` } // init initializes the required fields in the AuthConfig if they are not diff --git a/authority/config/config_test.go b/authority/config/config_test.go index 735ac33e..a5b60513 100644 --- a/authority/config/config_test.go +++ b/authority/config/config_test.go @@ -8,12 +8,14 @@ import ( "github.com/smallstep/assert" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/jose" + + _ "github.com/smallstep/certificates/cas" ) func TestConfigValidate(t *testing.T) { - maxjwk, err := jose.ReadKey("testdata/secrets/max_pub.jwk") + maxjwk, err := jose.ReadKey("../testdata/secrets/max_pub.jwk") assert.FatalError(t, err) - clijwk, err := jose.ReadKey("testdata/secrets/step_cli_key_pub.jwk") + clijwk, err := jose.ReadKey("../testdata/secrets/step_cli_key_pub.jwk") assert.FatalError(t, err) ac := &AuthConfig{ Provisioners: provisioner.List{ @@ -39,9 +41,9 @@ func TestConfigValidate(t *testing.T) { "empty-address": func(t *testing.T) ConfigValidateTest { return ConfigValidateTest{ config: &Config{ - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -53,9 +55,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -67,8 +69,8 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -80,8 +82,8 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -93,8 +95,8 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -106,9 +108,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", Password: "pass", AuthorityConfig: ac, }, @@ -119,9 +121,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -133,9 +135,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -148,9 +150,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -177,9 +179,9 @@ func TestConfigValidate(t *testing.T) { return ConfigValidateTest{ config: &Config{ Address: "127.0.0.1:443", - Root: []string{"testdata/secrets/root_ca.crt"}, - IntermediateCert: "testdata/secrets/intermediate_ca.crt", - IntermediateKey: "testdata/secrets/intermediate_ca_key", + Root: []string{"../testdata/secrets/root_ca.crt"}, + IntermediateCert: "../testdata/secrets/intermediate_ca.crt", + IntermediateKey: "../testdata/secrets/intermediate_ca_key", DNSNames: []string{"test.smallstep.com"}, Password: "pass", AuthorityConfig: ac, @@ -207,6 +209,8 @@ func TestConfigValidate(t *testing.T) { } } else { if assert.Nil(t, tc.err) { + fmt.Printf("tc.tls = %+v\n", tc.tls) + fmt.Printf("*tc.config.TLS = %+v\n", *tc.config.TLS) assert.Equals(t, *tc.config.TLS, tc.tls) } } @@ -224,9 +228,9 @@ func TestAuthConfigValidate(t *testing.T) { CommonName: "test", } - maxjwk, err := jose.ReadKey("testdata/secrets/max_pub.jwk") + maxjwk, err := jose.ReadKey("../testdata/secrets/max_pub.jwk") assert.FatalError(t, err) - clijwk, err := jose.ReadKey("testdata/secrets/step_cli_key_pub.jwk") + clijwk, err := jose.ReadKey("../testdata/secrets/step_cli_key_pub.jwk") assert.FatalError(t, err) p := provisioner.List{ &provisioner.JWK{ diff --git a/authority/config/ssh_test.go b/authority/config/ssh_test.go new file mode 100644 index 00000000..2c4c8eac --- /dev/null +++ b/authority/config/ssh_test.go @@ -0,0 +1,73 @@ +package config + +import ( + "reflect" + "testing" + + "github.com/smallstep/assert" + "go.step.sm/crypto/jose" + "golang.org/x/crypto/ssh" +) + +func TestSSHPublicKey_Validate(t *testing.T) { + key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) + assert.FatalError(t, err) + + type fields struct { + Type string + Federated bool + Key jose.JSONWebKey + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + {"user", fields{"user", true, key.Public()}, false}, + {"host", fields{"host", false, key.Public()}, false}, + {"empty", fields{"", true, key.Public()}, true}, + {"badType", fields{"bad", false, key.Public()}, true}, + {"badKey", fields{"user", false, *key}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &SSHPublicKey{ + Type: tt.fields.Type, + Federated: tt.fields.Federated, + Key: tt.fields.Key, + } + if err := k.Validate(); (err != nil) != tt.wantErr { + t.Errorf("SSHPublicKey.Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestSSHPublicKey_PublicKey(t *testing.T) { + key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) + assert.FatalError(t, err) + pub, err := ssh.NewPublicKey(key.Public().Key) + assert.FatalError(t, err) + + type fields struct { + publicKey ssh.PublicKey + } + tests := []struct { + name string + fields fields + want ssh.PublicKey + }{ + {"ok", fields{pub}, pub}, + {"nil", fields{nil}, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := &SSHPublicKey{ + publicKey: tt.fields.publicKey, + } + if got := k.PublicKey(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SSHPublicKey.PublicKey() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/authority/config/tls_options.go b/authority/config/tls_options.go index 5b0575d6..996b5834 100644 --- a/authority/config/tls_options.go +++ b/authority/config/tls_options.go @@ -34,6 +34,18 @@ var ( "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", } + // DefaultTLSOptions represents the default TLS version as well as the cipher + // suites used in the TLS certificates. + DefaultTLSOptions = TLSOptions{ + CipherSuites: CipherSuites{ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + }, + MinVersion: 1.2, + MaxVersion: 1.2, + Renegotiation: false, + } ) // TLSVersion represents a TLS version number. diff --git a/authority/mgmt/api/handler.go b/authority/mgmt/api/handler.go deleted file mode 100644 index ad8b35ad..00000000 --- a/authority/mgmt/api/handler.go +++ /dev/null @@ -1,47 +0,0 @@ -package api - -import ( - "time" - - "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority" - "github.com/smallstep/certificates/authority/mgmt" -) - -// Clock that returns time in UTC rounded to seconds. -type Clock struct{} - -// Now returns the UTC time rounded to seconds. -func (c *Clock) Now() time.Time { - return time.Now().UTC().Truncate(time.Second) -} - -var clock Clock - -// Handler is the ACME API request handler. -type Handler struct { - db mgmt.DB - auth *authority.Authority -} - -// NewHandler returns a new Authority Config Handler. -func NewHandler(auth *authority.Authority) api.RouterHandler { - return &Handler{auth.GetAdminDatabase(), auth} -} - -// Route traffic and implement the Router interface. -func (h *Handler) Route(r api.Router) { - // Provisioners - r.MethodFunc("GET", "/provisioners/{name}", h.GetProvisioner) - r.MethodFunc("GET", "/provisioners", h.GetProvisioners) - r.MethodFunc("POST", "/provisioners", h.CreateProvisioner) - r.MethodFunc("PUT", "/provisioners/{name}", h.UpdateProvisioner) - r.MethodFunc("DELETE", "/provisioners/{name}", h.DeleteProvisioner) - - // Admins - r.MethodFunc("GET", "/admins/{id}", h.GetAdmin) - r.MethodFunc("GET", "/admins", h.GetAdmins) - r.MethodFunc("POST", "/admins", h.CreateAdmin) - r.MethodFunc("PATCH", "/admins/{id}", h.UpdateAdmin) - r.MethodFunc("DELETE", "/admins/{id}", h.DeleteAdmin) -} diff --git a/authority/mgmt/api/provisioner.go b/authority/mgmt/api/provisioner.go deleted file mode 100644 index 7964d36b..00000000 --- a/authority/mgmt/api/provisioner.go +++ /dev/null @@ -1,199 +0,0 @@ -package api - -import ( - "fmt" - "net/http" - - "github.com/go-chi/chi" - "github.com/smallstep/certificates/api" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/linkedca" -) - -// CreateProvisionerRequest represents the body for a CreateProvisioner request. -type CreateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *linkedca.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` -} - -// Validate validates a new-provisioner request body. -func (cpr *CreateProvisionerRequest) Validate(c *provisioner.Collection) error { - if _, ok := c.LoadByName(cpr.Name); ok { - return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", cpr.Name) - } - return nil -} - -// GetProvisionersResponse is the type for GET /admin/provisioners responses. -type GetProvisionersResponse struct { - Provisioners provisioner.List `json:"provisioners"` - NextCursor string `json:"nextCursor"` -} - -// UpdateProvisionerRequest represents the body for a UpdateProvisioner request. -type UpdateProvisionerRequest struct { - Type string `json:"type"` - Name string `json:"name"` - Claims *linkedca.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` -} - -// Validate validates a update-provisioner request body. -func (upr *UpdateProvisionerRequest) Validate(c *provisioner.Collection) error { - if _, ok := c.LoadByName(upr.Name); ok { - return mgmt.NewError(mgmt.ErrorBadRequestType, "provisioner with name %s already exists", upr.Name) - } - return nil -} - -// GetProvisioner returns the requested provisioner, or an error. -func (h *Handler) GetProvisioner(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - name := chi.URLParam(r, "name") - - p, ok := h.auth.GetProvisionerCollection().LoadByName(name) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) - return - } - - prov, err := h.db.GetProvisioner(ctx, p.GetID()) - if err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, prov) -} - -// GetProvisioners returns all provisioners associated with the authority. -func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { - cursor, limit, err := api.ParseCursor(r) - if err != nil { - api.WriteError(w, mgmt.WrapError(mgmt.ErrorBadRequestType, err, - "error parsing cursor / limt query params")) - return - } - - p, next, err := h.auth.GetProvisioners(cursor, limit) - if err != nil { - api.WriteError(w, errs.InternalServerErr(err)) - return - } - api.JSON(w, &GetProvisionersResponse{ - Provisioners: p, - NextCursor: next, - }) -} - -// CreateProvisioner creates a new prov. -func (h *Handler) CreateProvisioner(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - var prov = new(linkedca.Provisioner) - if err := api.ReadJSON(r.Body, prov); err != nil { - api.WriteError(w, err) - return - } - - // TODO: validate - - // TODO: fix this - prov.Claims = mgmt.NewDefaultClaims() - - if err := h.db.CreateProvisioner(ctx, prov); err != nil { - api.WriteError(w, err) - return - } - api.JSONStatus(w, prov, http.StatusCreated) - - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } -} - -// DeleteProvisioner deletes a provisioner. -func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { - name := chi.URLParam(r, "name") - - p, ok := h.auth.GetProvisionerCollection().LoadByName(name) - if !ok { - api.WriteError(w, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", name)) - return - } - - c := h.auth.GetAdminCollection() - if c.SuperCount() == c.SuperCountByProvisioner(name) { - api.WriteError(w, mgmt.NewError(mgmt.ErrorBadRequestType, - "cannot remove provisioner %s because no super admins will remain", name)) - return - } - - ctx := r.Context() - if err := h.db.DeleteProvisioner(ctx, p.GetID()); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting provisioner %s", name)) - return - } - - // Delete all admins associated with the provisioner. - admins, ok := c.LoadByProvisioner(name) - if ok { - for _, adm := range admins { - if err := h.db.DeleteAdmin(ctx, adm.Id); err != nil { - api.WriteError(w, mgmt.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, name)) - return - } - } - } - - api.JSON(w, &DeleteResponse{Status: "ok"}) - - if err := h.auth.ReloadAuthConfig(ctx); err != nil { - fmt.Printf("err = %+v\n", err) - } -} - -// UpdateProvisioner updates an existing prov. -func (h *Handler) UpdateProvisioner(w http.ResponseWriter, r *http.Request) { - /* - ctx := r.Context() - id := chi.URLParam(r, "id") - - var body UpdateProvisionerRequest - 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 prov, err := h.db.GetProvisioner(ctx, id); err != nil { - api.WriteError(w, err) - return - } - - prov.Claims = body.Claims - prov.Details = body.Provisioner - prov.X509Template = body.X509Template - prov.SSHTemplate = body.SSHTemplate - prov.Status = body.Status - - if err := h.db.UpdateProvisioner(ctx, prov); err != nil { - api.WriteError(w, err) - return - } - api.JSON(w, prov) - */ -} diff --git a/authority/mgmt/config.go b/authority/mgmt/config.go deleted file mode 100644 index 3c322415..00000000 --- a/authority/mgmt/config.go +++ /dev/null @@ -1,48 +0,0 @@ -package mgmt - -const ( - // DefaultAuthorityID is the default AuthorityID. This will be the ID - // of the first Authority created, as well as the default AuthorityID - // if one is not specified in the configuration. - DefaultAuthorityID = "00000000-0000-0000-0000-000000000000" -) - -/* -func CreateAuthority(ctx context.Context, db DB, options ...AuthorityOption) (*AuthConfig, error) { - ac := NewDefaultAuthConfig() - - for _, o := range options { - if err := o(ac); err != nil { - return nil, err - } - } - - if err := db.CreateAuthConfig(ctx, ac); err != nil { - return nil, errors.Wrap(err, "error creating authConfig") - } - - // Generate default JWK provisioner. - - provOpts := []ProvisionerOption{WithPassword("pass")} - prov, err := CreateProvisioner(ctx, db, "JWK", "changeme", provOpts...) - if err != nil { - // TODO should we try to clean up? - return nil, WrapErrorISE(err, "error creating first provisioner") - } - - adm := &Admin{ - ProvisionerID: prov.ID, - Subject: "Change Me", - Type: AdminTypeSuper, - } - if err := db.CreateAdmin(ctx, adm); err != nil { - // TODO should we try to clean up? - return nil, WrapErrorISE(err, "error creating first admin") - } - - ac.Provisioners = []*Provisioner{prov} - ac.Admins = []*Admin{adm} - - return ac, nil -} -*/ diff --git a/authority/mgmt/db/nosql/provisioner.go b/authority/mgmt/db/nosql/provisioner.go deleted file mode 100644 index fa5eaf2a..00000000 --- a/authority/mgmt/db/nosql/provisioner.go +++ /dev/null @@ -1,219 +0,0 @@ -package nosql - -import ( - "context" - "encoding/json" - "time" - - "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/mgmt" - "github.com/smallstep/certificates/linkedca" - "github.com/smallstep/nosql" -) - -// dbProvisioner is the database representation of a Provisioner type. -type dbProvisioner struct { - ID string `json:"id"` - AuthorityID string `json:"authorityID"` - Type linkedca.Provisioner_Type `json:"type"` - // Name is the key - Name string `json:"name"` - Claims *linkedca.Claims `json:"claims"` - Details []byte `json:"details"` - X509Template []byte `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate []byte `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - CreatedAt time.Time `json:"createdAt"` - DeletedAt time.Time `json:"deletedAt"` -} - -type provisionerNameID struct { - Name string `json:"name"` - ID string `json:"id"` -} - -func (dbp *dbProvisioner) clone() *dbProvisioner { - u := *dbp - return &u -} - -func (db *DB) getDBProvisionerBytes(ctx context.Context, id string) ([]byte, error) { - data, err := db.db.Get(authorityProvisionersTable, []byte(id)) - if nosql.IsErrNotFound(err) { - return nil, mgmt.NewError(mgmt.ErrorNotFoundType, "provisioner %s not found", id) - } else if err != nil { - return nil, errors.Wrapf(err, "error loading provisioner %s", id) - } - return data, nil -} - -func (db *DB) getDBProvisioner(ctx context.Context, id string) (*dbProvisioner, error) { - data, err := db.getDBProvisionerBytes(ctx, id) - if err != nil { - return nil, err - } - dbp, err := unmarshalDBProvisioner(data, id) - if err != nil { - return nil, err - } - if !dbp.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", id) - } - if dbp.AuthorityID != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "provisioner %s is not owned by authority %s", dbp.ID, db.authorityID) - } - return dbp, nil -} - -// GetProvisioner retrieves and unmarshals a provisioner from the database. -func (db *DB) GetProvisioner(ctx context.Context, id string) (*linkedca.Provisioner, error) { - data, err := db.getDBProvisionerBytes(ctx, id) - if err != nil { - return nil, err - } - - prov, err := unmarshalProvisioner(data, id) - if err != nil { - return nil, err - } - if prov.AuthorityId != db.authorityID { - return nil, mgmt.NewError(mgmt.ErrorAuthorityMismatchType, - "provisioner %s is not owned by authority %s", prov.Id, db.authorityID) - } - return prov, nil -} - -func unmarshalDBProvisioner(data []byte, name string) (*dbProvisioner, error) { - var dbp = new(dbProvisioner) - if err := json.Unmarshal(data, dbp); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling provisioner %s into dbProvisioner", name) - } - if !dbp.DeletedAt.IsZero() { - return nil, mgmt.NewError(mgmt.ErrorDeletedType, "provisioner %s is deleted", name) - } - return dbp, nil -} - -func unmarshalProvisioner(data []byte, name string) (*linkedca.Provisioner, error) { - dbp, err := unmarshalDBProvisioner(data, name) - if err != nil { - return nil, err - } - - details, err := linkedca.UnmarshalProvisionerDetails(dbp.Type, dbp.Details) - if err != nil { - return nil, err - } - - prov := &linkedca.Provisioner{ - Id: dbp.ID, - AuthorityId: dbp.AuthorityID, - Type: dbp.Type, - Name: dbp.Name, - Claims: dbp.Claims, - Details: details, - X509Template: dbp.X509Template, - X509TemplateData: dbp.X509TemplateData, - SshTemplate: dbp.SSHTemplate, - SshTemplateData: dbp.SSHTemplateData, - } - return prov, nil -} - -// GetProvisioners retrieves and unmarshals all active (not deleted) provisioners -// from the database. -func (db *DB) GetProvisioners(ctx context.Context) ([]*linkedca.Provisioner, error) { - dbEntries, err := db.db.List(authorityProvisionersTable) - if err != nil { - return nil, mgmt.WrapErrorISE(err, "error loading provisioners") - } - var provs []*linkedca.Provisioner - for _, entry := range dbEntries { - prov, err := unmarshalProvisioner(entry.Value, string(entry.Key)) - if err != nil { - return nil, err - } - if prov.AuthorityId != db.authorityID { - continue - } - provs = append(provs, prov) - } - return provs, nil -} - -// CreateProvisioner stores a new provisioner to the database. -func (db *DB) CreateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { - var err error - prov.Id, err = randID() - if err != nil { - return errors.Wrap(err, "error generating random id for provisioner") - } - - details, err := json.Marshal(prov.Details.GetData()) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when creating provisioner %s", prov.Name) - } - - dbp := &dbProvisioner{ - ID: prov.Id, - AuthorityID: db.authorityID, - Type: prov.Type, - Name: prov.Name, - Claims: prov.Claims, - Details: details, - X509Template: prov.X509Template, - X509TemplateData: prov.X509TemplateData, - SSHTemplate: prov.SshTemplate, - SSHTemplateData: prov.SshTemplateData, - CreatedAt: clock.Now(), - } - - if err := db.save(ctx, prov.Id, dbp, nil, "provisioner", authorityProvisionersTable); err != nil { - return mgmt.WrapErrorISE(err, "error creating provisioner %s", prov.Name) - } - - return nil -} - -// UpdateProvisioner saves an updated provisioner to the database. -func (db *DB) UpdateProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { - old, err := db.getDBProvisioner(ctx, prov.Id) - if err != nil { - return err - } - - nu := old.clone() - - nu.Type = prov.Type - nu.Name = prov.Name - nu.Claims = prov.Claims - nu.Details, err = json.Marshal(prov.Details) - if err != nil { - return mgmt.WrapErrorISE(err, "error marshaling details when updating provisioner %s", prov.Name) - } - nu.X509Template = prov.X509Template - nu.X509TemplateData = prov.X509TemplateData - nu.SSHTemplate = prov.SshTemplate - nu.SSHTemplateData = prov.SshTemplateData - - if err := db.save(ctx, prov.Id, nu, old, "provisioner", authorityProvisionersTable); err != nil { - return mgmt.WrapErrorISE(err, "error updating provisioner %s", prov.Name) - } - - return nil -} - -// DeleteProvisioner saves an updated admin to the database. -func (db *DB) DeleteProvisioner(ctx context.Context, id string) error { - old, err := db.getDBProvisioner(ctx, id) - if err != nil { - return err - } - - nu := old.clone() - nu.DeletedAt = clock.Now() - - return db.save(ctx, old.ID, nu, old, "provisioner", authorityProvisionersTable) -} diff --git a/authority/mgmt/provisioner.go b/authority/mgmt/provisioner.go deleted file mode 100644 index 2177e116..00000000 --- a/authority/mgmt/provisioner.go +++ /dev/null @@ -1,116 +0,0 @@ -package mgmt - -import ( - "context" - - "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/linkedca" - "go.step.sm/crypto/jose" -) - -/* -type unmarshalProvisioner struct { - ID string `json:"-"` - AuthorityID string `json:"-"` - Type string `json:"type"` - Name string `json:"name"` - Claims *Claims `json:"claims"` - Details json.RawMessage `json:"details"` - X509Template string `json:"x509Template"` - X509TemplateData []byte `json:"x509TemplateData"` - SSHTemplate string `json:"sshTemplate"` - SSHTemplateData []byte `json:"sshTemplateData"` - Status status.Type `json:"status"` -} - -type typ struct { - Type linkedca.Provisioner_Type `json:"type"` -} - -// UnmarshalJSON implements the Unmarshal interface. -func (p *Provisioner) UnmarshalJSON(b []byte) error { - var ( - err error - up = new(unmarshalProvisioner) - ) - if err = json.Unmarshal(b, up); err != nil { - return WrapErrorISE(err, "error unmarshaling provisioner to intermediate type") - } - p.Details, err = UnmarshalProvisionerDetails(up.Details) - if err = json.Unmarshal(b, up); err != nil { - return WrapErrorISE(err, "error unmarshaling provisioner details") - } - - p.ID = up.ID - p.AuthorityID = up.AuthorityID - p.Type = up.Type - p.Name = up.Name - p.Claims = up.Claims - p.X509Template = up.X509Template - p.X509TemplateData = up.X509TemplateData - p.SSHTemplate = up.SSHTemplate - p.SSHTemplateData = up.SSHTemplateData - p.Status = up.Status - - return nil -} -*/ - -func NewDefaultClaims() *linkedca.Claims { - return &linkedca.Claims{ - X509: &linkedca.X509Claims{ - Durations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinTLSDur.String(), - Max: config.GlobalProvisionerClaims.MaxTLSDur.String(), - Default: config.GlobalProvisionerClaims.DefaultTLSDur.String(), - }, - }, - Ssh: &linkedca.SSHClaims{ - UserDurations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinUserSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxUserSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultUserSSHDur.String(), - }, - HostDurations: &linkedca.Durations{ - Min: config.GlobalProvisionerClaims.MinHostSSHDur.String(), - Max: config.GlobalProvisionerClaims.MaxHostSSHDur.String(), - Default: config.GlobalProvisionerClaims.DefaultHostSSHDur.String(), - }, - }, - DisableRenewal: config.DefaultDisableRenewal, - } -} - -func CreateFirstProvisioner(ctx context.Context, db DB, password string) (*linkedca.Provisioner, error) { - jwk, jwe, err := jose.GenerateDefaultKeyPair([]byte(password)) - if err != nil { - return nil, WrapErrorISE(err, "error generating JWK key pair") - } - - jwkPubBytes, err := jwk.MarshalJSON() - if err != nil { - return nil, WrapErrorISE(err, "error marshaling JWK") - } - jwePrivStr, err := jwe.CompactSerialize() - if err != nil { - return nil, WrapErrorISE(err, "error serializing JWE") - } - - p := &linkedca.Provisioner{ - Name: "Admin JWK", - Type: linkedca.Provisioner_JWK, - Claims: NewDefaultClaims(), - Details: &linkedca.ProvisionerDetails{ - Data: &linkedca.ProvisionerDetails_JWK{ - JWK: &linkedca.JWKProvisioner{ - PublicKey: jwkPubBytes, - EncryptedPrivateKey: []byte(jwePrivStr), - }, - }, - }, - } - if err := db.CreateProvisioner(ctx, p); err != nil { - return nil, WrapErrorISE(err, "error creating provisioner") - } - return p, nil -} diff --git a/authority/options.go b/authority/options.go index cc2f64af..4e9fbdbc 100644 --- a/authority/options.go +++ b/authority/options.go @@ -7,8 +7,8 @@ import ( "encoding/pem" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/cas" casapi "github.com/smallstep/certificates/cas/apiv1" @@ -189,7 +189,7 @@ func WithX509FederatedBundle(pemCerts []byte) Option { } // WithAdminDB is an option to set the database backing the admin APIs. -func WithAdminDB(db mgmt.DB) Option { +func WithAdminDB(db admin.DB) Option { return func(a *Authority) error { a.adminDB = db return nil diff --git a/authority/provisioner/acme.go b/authority/provisioner/acme.go index 4109d217..d81b0231 100644 --- a/authority/provisioner/acme.go +++ b/authority/provisioner/acme.go @@ -105,7 +105,7 @@ func (p *ACME) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e // certificate was configured to allow renewals. func (p *ACME) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("acme.AuthorizeRenew; renew is disabled for acme provisioner %s", p.GetID()) + return errs.Unauthorized("acme.AuthorizeRenew; renew is disabled for acme provisioner '%s'", p.GetName()) } return nil } diff --git a/authority/provisioner/acme_test.go b/authority/provisioner/acme_test.go index 7b669d8d..bd173f87 100644 --- a/authority/provisioner/acme_test.go +++ b/authority/provisioner/acme_test.go @@ -61,7 +61,7 @@ func TestACME_Init(t *testing.T) { "fail-bad-claims": func(t *testing.T) ProvisionerValidateTest { return ProvisionerValidateTest{ p: &ACME{Name: "foo", Type: "bar", Claims: &Claims{DefaultTLSDur: &Duration{0}}}, - err: errors.New("claims: DefaultTLSCertDuration must be greater than 0"), + err: errors.New("claims: MinTLSCertDuration must be greater than 0"), } }, "ok": func(t *testing.T) ProvisionerValidateTest { @@ -110,7 +110,7 @@ func TestACME_AuthorizeRenew(t *testing.T) { p: p, cert: &x509.Certificate{}, code: http.StatusUnauthorized, - err: errors.Errorf("acme.AuthorizeRenew; renew is disabled for acme provisioner %s", p.GetID()), + err: errors.Errorf("acme.AuthorizeRenew; renew is disabled for acme provisioner '%s'", p.GetName()), } }, "ok": func(t *testing.T) test { diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 8a443554..c1c77ce5 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -296,7 +296,7 @@ func (p *AWS) GetTokenID(token string) (string, error) { } // Use provisioner + instance-id as the identifier. - unique := fmt.Sprintf("%s.%s", p.GetID(), payload.document.InstanceID) + unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), payload.document.InstanceID) sum := sha256.Sum256([]byte(unique)) return strings.ToLower(hex.EncodeToString(sum[:])), nil } @@ -344,7 +344,7 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { return "", err } - audience, err := generateSignAudience(caURL, p.GetID()) + audience, err := generateSignAudience(caURL, p.GetIDForToken()) if err != nil { return "", err } @@ -352,7 +352,7 @@ func (p *AWS) GetIdentityToken(subject, caURL string) (string, error) { // Create unique ID for Trust On First Use (TOFU). Only the first instance // per provisioner is allowed as we don't have a way to trust the given // sans. - unique := fmt.Sprintf("%s.%s", p.GetID(), idoc.InstanceID) + unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), idoc.InstanceID) sum := sha256.Sum256([]byte(unique)) // Create a JWT from the identity document @@ -407,7 +407,7 @@ func (p *AWS) Init(config Config) (err error) { if p.config, err = newAWSConfig(p.IIDRoots); err != nil { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) // validate IMDS versions if len(p.IMDSVersions) == 0 { @@ -484,7 +484,7 @@ func (p *AWS) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // certificate was configured to allow renewals. func (p *AWS) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("aws.AuthorizeRenew; renew is disabled for aws provisioner %s", p.GetID()) + return errs.Unauthorized("aws.AuthorizeRenew; renew is disabled for aws provisioner '%s'", p.GetName()) } return nil } @@ -697,7 +697,7 @@ func (p *AWS) authorizeToken(token string) (*awsPayload, error) { // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("aws.AuthorizeSSHSign; ssh ca is disabled for aws provisioner %s", p.GetID()) + return nil, errs.Unauthorized("aws.AuthorizeSSHSign; ssh ca is disabled for aws provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token) if err != nil { diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index b077d735..230f246f 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -334,7 +334,7 @@ func (p *Azure) AuthorizeSign(ctx context.Context, token string) ([]SignOption, // certificate was configured to allow renewals. func (p *Azure) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("azure.AuthorizeRenew; renew is disabled for azure provisioner %s", p.GetID()) + return errs.Unauthorized("azure.AuthorizeRenew; renew is disabled for azure provisioner '%s'", p.GetName()) } return nil } @@ -342,7 +342,7 @@ func (p *Azure) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) erro // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *Azure) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("azure.AuthorizeSSHSign; sshCA is disabled for provisioner %s", p.GetID()) + return nil, errs.Unauthorized("azure.AuthorizeSSHSign; sshCA is disabled for provisioner '%s'", p.GetName()) } _, name, _, err := p.authorizeToken(token) diff --git a/authority/provisioner/claims.go b/authority/provisioner/claims.go index 6c87792c..629a313c 100644 --- a/authority/provisioner/claims.go +++ b/authority/provisioner/claims.go @@ -7,27 +7,6 @@ import ( "golang.org/x/crypto/ssh" ) -type _Claims struct { - *X509Claims `json:"x509Claims"` - *SSHClaims `json:"sshClaims"` - DisableRenewal *bool `json:"disableRenewal"` -} - -type X509Claims struct { - Durations *Durations `json:"durations"` -} - -type SSHClaims struct { - UserDuration *Durations `json:"userDurations"` - HostDuration *Durations `json:"hostDuration"` -} - -type Durations struct { - Min string `json:"min"` - Max string `json:"max"` - Default string `json:"default"` -} - // Claims so that individual provisioners can override global claims. type Claims struct { // TLS CA properties @@ -92,6 +71,9 @@ func (c *Claimer) DefaultTLSCertDuration() time.Duration { // minimum from the authority configuration will be used. func (c *Claimer) MinTLSCertDuration() time.Duration { if c.claims == nil || c.claims.MinTLSDur == nil { + if c.claims != nil && c.claims.DefaultTLSDur != nil && c.claims.DefaultTLSDur.Duration < c.global.MinTLSDur.Duration { + return c.claims.DefaultTLSDur.Duration + } return c.global.MinTLSDur.Duration } return c.claims.MinTLSDur.Duration @@ -102,6 +84,9 @@ func (c *Claimer) MinTLSCertDuration() time.Duration { // maximum from the authority configuration will be used. func (c *Claimer) MaxTLSCertDuration() time.Duration { if c.claims == nil || c.claims.MaxTLSDur == nil { + if c.claims != nil && c.claims.DefaultTLSDur != nil && c.claims.DefaultTLSDur.Duration > c.global.MaxTLSDur.Duration { + return c.claims.DefaultTLSDur.Duration + } return c.global.MaxTLSDur.Duration } return c.claims.MaxTLSDur.Duration @@ -147,6 +132,9 @@ func (c *Claimer) DefaultUserSSHCertDuration() time.Duration { // global minimum from the authority configuration will be used. func (c *Claimer) MinUserSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MinUserSSHDur == nil { + if c.claims != nil && c.claims.DefaultUserSSHDur != nil && c.claims.DefaultUserSSHDur.Duration < c.global.MinUserSSHDur.Duration { + return c.claims.DefaultUserSSHDur.Duration + } return c.global.MinUserSSHDur.Duration } return c.claims.MinUserSSHDur.Duration @@ -157,6 +145,9 @@ func (c *Claimer) MinUserSSHCertDuration() time.Duration { // global maximum from the authority configuration will be used. func (c *Claimer) MaxUserSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MaxUserSSHDur == nil { + if c.claims != nil && c.claims.DefaultUserSSHDur != nil && c.claims.DefaultUserSSHDur.Duration > c.global.MaxUserSSHDur.Duration { + return c.claims.DefaultUserSSHDur.Duration + } return c.global.MaxUserSSHDur.Duration } return c.claims.MaxUserSSHDur.Duration @@ -177,6 +168,9 @@ func (c *Claimer) DefaultHostSSHCertDuration() time.Duration { // global minimum from the authority configuration will be used. func (c *Claimer) MinHostSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MinHostSSHDur == nil { + if c.claims != nil && c.claims.DefaultHostSSHDur != nil && c.claims.DefaultHostSSHDur.Duration < c.global.MinHostSSHDur.Duration { + return c.claims.DefaultHostSSHDur.Duration + } return c.global.MinHostSSHDur.Duration } return c.claims.MinHostSSHDur.Duration @@ -187,6 +181,9 @@ func (c *Claimer) MinHostSSHCertDuration() time.Duration { // global maximum from the authority configuration will be used. func (c *Claimer) MaxHostSSHCertDuration() time.Duration { if c.claims == nil || c.claims.MaxHostSSHDur == nil { + if c.claims != nil && c.claims.DefaultHostSSHDur != nil && c.claims.DefaultHostSSHDur.Duration > c.global.MaxHostSSHDur.Duration { + return c.claims.DefaultHostSSHDur.Duration + } return c.global.MaxHostSSHDur.Duration } return c.claims.MaxHostSSHDur.Duration diff --git a/authority/provisioner/collection.go b/authority/provisioner/collection.go index eecacf69..3ba98a23 100644 --- a/authority/provisioner/collection.go +++ b/authority/provisioner/collection.go @@ -12,7 +12,7 @@ import ( "strings" "sync" - "github.com/pkg/errors" + "github.com/smallstep/certificates/authority/admin" "go.step.sm/crypto/jose" ) @@ -148,24 +148,7 @@ func (c *Collection) LoadByCertificate(cert *x509.Certificate) (Interface, bool) if _, err := asn1.Unmarshal(e.Value, &provisioner); err != nil { return nil, false } - switch Type(provisioner.Type) { - case TypeJWK: - return c.Load(string(provisioner.Name) + ":" + string(provisioner.CredentialID)) - case TypeAWS: - return c.Load("aws/" + string(provisioner.Name)) - case TypeGCP: - return c.Load("gcp/" + string(provisioner.Name)) - case TypeACME: - return c.Load("acme/" + string(provisioner.Name)) - case TypeSCEP: - return c.Load("scep/" + string(provisioner.Name)) - case TypeX5C: - return c.Load("x5c/" + string(provisioner.Name)) - case TypeK8sSA: - return c.Load(K8sSAID) - default: - return c.Load(string(provisioner.CredentialID)) - } + return c.LoadByName(string(provisioner.Name)) } } @@ -190,18 +173,21 @@ func (c *Collection) LoadEncryptedKey(keyID string) (string, bool) { func (c *Collection) Store(p Interface) error { // Store provisioner always in byID. ID must be unique. if _, loaded := c.byID.LoadOrStore(p.GetID(), p); loaded { - return errors.New("cannot add multiple provisioners with the same id") + return admin.NewError(admin.ErrorBadRequestType, + "cannot add multiple provisioners with the same id") } // Store provisioner always by name. if _, loaded := c.byName.LoadOrStore(p.GetName(), p); loaded { c.byID.Delete(p.GetID()) - return errors.New("cannot add multiple provisioners with the same name") + return admin.NewError(admin.ErrorBadRequestType, + "cannot add multiple provisioners with the same name") } // Store provisioner always by ID presented in token. if _, loaded := c.byTokenID.LoadOrStore(p.GetIDForToken(), p); loaded { c.byID.Delete(p.GetID()) c.byName.Delete(p.GetName()) - return errors.New("cannot add multiple provisioners with the same token identifier") + return admin.NewError(admin.ErrorBadRequestType, + "cannot add multiple provisioners with the same token identifier") } // Store provisioner in byKey if EncryptedKey is defined. @@ -225,6 +211,65 @@ func (c *Collection) Store(p Interface) error { return nil } +// Remove deletes an provisioner from all associated collections and lists. +func (c *Collection) Remove(id string) error { + prov, ok := c.Load(id) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id) + } + + var found bool + for i, elem := range c.sorted { + if elem.provisioner.GetID() == id { + // Remove index in sorted list + copy(c.sorted[i:], c.sorted[i+1:]) // Shift a[i+1:] left one index. + c.sorted[len(c.sorted)-1] = uidProvisioner{} // Erase last element (write zero value). + c.sorted = c.sorted[:len(c.sorted)-1] // Truncate slice. + found = true + break + } + } + if !found { + return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found in sorted list", prov.GetName()) + } + + c.byID.Delete(id) + c.byName.Delete(prov.GetName()) + c.byTokenID.Delete(prov.GetIDForToken()) + if kid, _, ok := prov.GetEncryptedKey(); ok { + c.byKey.Delete(kid) + } + + return nil +} + +// Update updates the given provisioner in all related lists and collections. +func (c *Collection) Update(nu Interface) error { + old, ok := c.Load(nu.GetID()) + if !ok { + return admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", nu.GetID()) + } + + if old.GetName() != nu.GetName() { + if _, ok := c.LoadByName(nu.GetName()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with name %s already exists", nu.GetName()) + } + } + if old.GetIDForToken() != nu.GetIDForToken() { + if _, ok := c.LoadByTokenID(nu.GetIDForToken()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with Token ID %s already exists", nu.GetIDForToken()) + } + } + + if err := c.Remove(old.GetID()); err != nil { + return err + } + + return c.Store(nu) +} + // Find implements pagination on a list of sorted provisioners. func (c *Collection) Find(cursor string, limit int) (List, string) { switch { diff --git a/authority/provisioner/collection_test.go b/authority/provisioner/collection_test.go index a0a79e92..348b797c 100644 --- a/authority/provisioner/collection_test.go +++ b/authority/provisioner/collection_test.go @@ -132,6 +132,7 @@ func TestCollection_LoadByToken(t *testing.T) { t.Run(tt.name, func(t *testing.T) { c := &Collection{ byID: tt.fields.byID, + byTokenID: tt.fields.byID, audiences: tt.fields.audiences, } got, got1 := c.LoadByToken(tt.args.token, tt.args.claims) @@ -153,10 +154,10 @@ func TestCollection_LoadByCertificate(t *testing.T) { p3, err := generateACME() assert.FatalError(t, err) - byID := new(sync.Map) - byID.Store(p1.GetID(), p1) - byID.Store(p2.GetID(), p2) - byID.Store(p3.GetID(), p3) + byName := new(sync.Map) + byName.Store(p1.GetName(), p1) + byName.Store(p2.GetName(), p2) + byName.Store(p3.GetName(), p3) ok1Ext, err := createProvisionerExtension(1, p1.Name, p1.Key.KeyID) assert.FatalError(t, err) @@ -186,7 +187,7 @@ func TestCollection_LoadByCertificate(t *testing.T) { } type fields struct { - byID *sync.Map + byName *sync.Map audiences Audiences } type args struct { @@ -199,17 +200,17 @@ func TestCollection_LoadByCertificate(t *testing.T) { want Interface want1 bool }{ - {"ok1", fields{byID, testAudiences}, args{ok1Cert}, p1, true}, - {"ok2", fields{byID, testAudiences}, args{ok2Cert}, p2, true}, - {"ok3", fields{byID, testAudiences}, args{ok3Cert}, p3, true}, - {"noExtension", fields{byID, testAudiences}, args{&x509.Certificate{}}, &noop{}, true}, - {"notFound", fields{byID, testAudiences}, args{notFoundCert}, nil, false}, - {"badCert", fields{byID, testAudiences}, args{badCert}, nil, false}, + {"ok1", fields{byName, testAudiences}, args{ok1Cert}, p1, true}, + {"ok2", fields{byName, testAudiences}, args{ok2Cert}, p2, true}, + {"ok3", fields{byName, testAudiences}, args{ok3Cert}, p3, true}, + {"noExtension", fields{byName, testAudiences}, args{&x509.Certificate{}}, &noop{}, true}, + {"notFound", fields{byName, testAudiences}, args{notFoundCert}, nil, false}, + {"badCert", fields{byName, testAudiences}, args{badCert}, nil, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Collection{ - byID: tt.fields.byID, + byName: tt.fields.byName, audiences: tt.fields.audiences, } got, got1 := c.LoadByCertificate(tt.args.cert) diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 6d19d052..1b599fb3 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -134,7 +134,7 @@ func (p *GCP) GetTokenID(token string) (string, error) { // Create unique ID for Trust On First Use (TOFU). Only the first instance // per provisioner is allowed as we don't have a way to trust the given // sans. - unique := fmt.Sprintf("%s.%s", p.GetID(), claims.Google.ComputeEngine.InstanceID) + unique := fmt.Sprintf("%s.%s", p.GetIDForToken(), claims.Google.ComputeEngine.InstanceID) sum := sha256.Sum256([]byte(unique)) return strings.ToLower(hex.EncodeToString(sum[:])), nil } @@ -168,7 +168,7 @@ func (p *GCP) GetIdentityURL(audience string) string { // GetIdentityToken does an HTTP request to the identity url. func (p *GCP) GetIdentityToken(subject, caURL string) (string, error) { - audience, err := generateSignAudience(caURL, p.GetID()) + audience, err := generateSignAudience(caURL, p.GetIDForToken()) if err != nil { return "", err } @@ -216,7 +216,7 @@ func (p *GCP) Init(config Config) error { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) return nil } @@ -277,7 +277,7 @@ func (p *GCP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // AuthorizeRenew returns an error if the renewal is disabled. func (p *GCP) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("gcp.AuthorizeRenew; renew is disabled for gcp provisioner %s", p.GetID()) + return errs.Unauthorized("gcp.AuthorizeRenew; renew is disabled for gcp provisioner '%s'", p.GetName()) } return nil } @@ -382,7 +382,7 @@ func (p *GCP) authorizeToken(token string) (*gcpPayload, error) { // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA is disabled for gcp provisioner %s", p.GetID()) + return nil, errs.Unauthorized("gcp.AuthorizeSSHSign; sshCA is disabled for gcp provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token) if err != nil { diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index d57ff4c1..56768fb7 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -194,7 +194,7 @@ func (p *JWK) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // certificate was configured to allow renewals. func (p *JWK) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("jwk.AuthorizeRenew; renew is disabled for jwk provisioner %s", p.GetID()) + return errs.Unauthorized("jwk.AuthorizeRenew; renew is disabled for jwk provisioner '%s'", p.GetName()) } return nil } @@ -202,7 +202,7 @@ func (p *JWK) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("jwk.AuthorizeSSHSign; sshCA is disabled for jwk provisioner %s", p.GetID()) + return nil, errs.Unauthorized("jwk.AuthorizeSSHSign; sshCA is disabled for jwk provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token, p.audiences.SSHSign) if err != nil { diff --git a/authority/provisioner/jwk_test.go b/authority/provisioner/jwk_test.go index 9198ff69..deae8f7a 100644 --- a/authority/provisioner/jwk_test.go +++ b/authority/provisioner/jwk_test.go @@ -77,7 +77,7 @@ func TestJWK_Init(t *testing.T) { "fail-bad-claims": func(t *testing.T) ProvisionerValidateTest { return ProvisionerValidateTest{ p: &JWK{Name: "foo", Type: "bar", Key: &jose.JSONWebKey{}, audiences: testAudiences, Claims: &Claims{DefaultTLSDur: &Duration{0}}}, - err: errors.New("claims: DefaultTLSCertDuration must be greater than 0"), + err: errors.New("claims: MinTLSCertDuration must be greater than 0"), } }, "ok": func(t *testing.T) ProvisionerValidateTest { diff --git a/authority/provisioner/k8sSA.go b/authority/provisioner/k8sSA.go index 876131e1..d260f5ec 100644 --- a/authority/provisioner/k8sSA.go +++ b/authority/provisioner/k8sSA.go @@ -111,12 +111,12 @@ func (p *K8sSA) Init(config Config) (err error) { } key, err := pemutil.ParseKey(pem.EncodeToMemory(block)) if err != nil { - return errors.Wrapf(err, "error parsing public key in provisioner %s", p.GetID()) + return errors.Wrapf(err, "error parsing public key in provisioner '%s'", p.GetName()) } switch q := key.(type) { case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: default: - return errors.Errorf("Unexpected public key type %T in provisioner %s", q, p.GetID()) + return errors.Errorf("Unexpected public key type %T in provisioner '%s'", q, p.GetName()) } p.pubKeys = append(p.pubKeys, key) } @@ -250,7 +250,7 @@ func (p *K8sSA) AuthorizeSign(ctx context.Context, token string) ([]SignOption, // AuthorizeRenew returns an error if the renewal is disabled. func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner %s", p.GetID()) + return errs.Unauthorized("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner '%s'", p.GetName()) } return nil } @@ -258,7 +258,7 @@ func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) erro // AuthorizeSSHSign validates an request for an SSH certificate. func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner %s", p.GetID()) + return nil, errs.Unauthorized("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token, p.audiences.SSHSign) if err != nil { diff --git a/authority/provisioner/k8sSA_test.go b/authority/provisioner/k8sSA_test.go index 03ae7eff..176cdfd3 100644 --- a/authority/provisioner/k8sSA_test.go +++ b/authority/provisioner/k8sSA_test.go @@ -198,7 +198,7 @@ func TestK8sSA_AuthorizeRenew(t *testing.T) { p: p, cert: &x509.Certificate{}, code: http.StatusUnauthorized, - err: errors.Errorf("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner %s", p.GetID()), + err: errors.Errorf("k8ssa.AuthorizeRenew; renew is disabled for k8sSA provisioner '%s'", p.GetName()), } }, "ok": func(t *testing.T) test { @@ -319,7 +319,7 @@ func TestK8sSA_AuthorizeSSHSign(t *testing.T) { p: p, token: "foo", code: http.StatusUnauthorized, - err: errors.Errorf("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner %s", p.GetID()), + err: errors.Errorf("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner '%s'", p.GetName()), } }, "fail/invalid-token": func(t *testing.T) test { diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index df3c1f9e..b6bca872 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -377,7 +377,7 @@ func (o *OIDC) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e // certificate was configured to allow renewals. func (o *OIDC) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if o.claimer.IsDisableRenewal() { - return errs.Unauthorized("oidc.AuthorizeRenew; renew is disabled for oidc provisioner %s", o.GetID()) + return errs.Unauthorized("oidc.AuthorizeRenew; renew is disabled for oidc provisioner '%s'", o.GetName()) } return nil } @@ -385,7 +385,7 @@ func (o *OIDC) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !o.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("oidc.AuthorizeSSHSign; sshCA is disabled for oidc provisioner %s", o.GetID()) + return nil, errs.Unauthorized("oidc.AuthorizeSSHSign; sshCA is disabled for oidc provisioner '%s'", o.GetName()) } claims, err := o.authorizeToken(token) if err != nil { diff --git a/authority/provisioner/scep.go b/authority/provisioner/scep.go index 7673ecc2..d4517627 100644 --- a/authority/provisioner/scep.go +++ b/authority/provisioner/scep.go @@ -11,6 +11,7 @@ import ( // SCEP provisioning flow type SCEP struct { *base + ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` @@ -26,8 +27,17 @@ type SCEP struct { secretChallengePassword string } -// GetID returns the provisioner unique identifier. -func (s SCEP) GetID() string { +// GetID returns the provisioner unique identifier. The name and credential id +// should uniquely identify any JWK provisioner. +func (s *SCEP) GetID() string { + if s.ID != "" { + return s.ID + } + return s.GetIDForToken() +} + +// GetIDForToken returns the provisioner unique identifier. +func (s SCEP) GetIDForToken() string { return "scep/" + s.Name } diff --git a/authority/provisioner/sshpop.go b/authority/provisioner/sshpop.go index dd9c7d1f..8bc76edf 100644 --- a/authority/provisioner/sshpop.go +++ b/authority/provisioner/sshpop.go @@ -101,7 +101,7 @@ func (p *SSHPOP) Init(config Config) error { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) p.db = config.DB p.sshPubKeys = config.SSHKeys return nil diff --git a/authority/provisioner/x5c.go b/authority/provisioner/x5c.go index 9e47aaed..a05f39c7 100644 --- a/authority/provisioner/x5c.go +++ b/authority/provisioner/x5c.go @@ -116,7 +116,7 @@ func (p *X5C) Init(config Config) error { // Verify that at least one root was found. if len(p.rootPool.Subjects()) == 0 { - return errors.Errorf("no x509 certificates found in roots attribute for provisioner %s", p.GetName()) + return errors.Errorf("no x509 certificates found in roots attribute for provisioner '%s'", p.GetName()) } // Update claims with global ones @@ -125,7 +125,7 @@ func (p *X5C) Init(config Config) error { return err } - p.audiences = config.Audiences.WithFragment(p.GetID()) + p.audiences = config.Audiences.WithFragment(p.GetIDForToken()) return nil } @@ -139,7 +139,8 @@ func (p *X5C) authorizeToken(token string, audiences []string) (*x5cPayload, err } verifiedChains, err := jwt.Headers[0].Certificates(x509.VerifyOptions{ - Roots: p.rootPool, + Roots: p.rootPool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }) if err != nil { return nil, errs.Wrap(http.StatusUnauthorized, err, @@ -234,7 +235,7 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er // AuthorizeRenew returns an error if the renewal is disabled. func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { - return errs.Unauthorized("x5c.AuthorizeRenew; renew is disabled for x5c provisioner %s", p.GetID()) + return errs.Unauthorized("x5c.AuthorizeRenew; renew is disabled for x5c provisioner '%s'", p.GetName()) } return nil } @@ -242,7 +243,7 @@ func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error // AuthorizeSSHSign returns the list of SignOption for a SignSSH request. func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) { if !p.claimer.IsSSHCAEnabled() { - return nil, errs.Unauthorized("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner %s", p.GetID()) + return nil, errs.Unauthorized("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner '%s'", p.GetName()) } claims, err := p.authorizeToken(token, p.audiences.SSHSign) diff --git a/authority/provisioner/x5c_test.go b/authority/provisioner/x5c_test.go index 5d288de5..2959f8c6 100644 --- a/authority/provisioner/x5c_test.go +++ b/authority/provisioner/x5c_test.go @@ -70,7 +70,7 @@ func TestX5C_Init(t *testing.T) { "fail/no-valid-root-certs": func(t *testing.T) ProvisionerValidateTest { return ProvisionerValidateTest{ p: &X5C{Name: "foo", Type: "bar", Roots: []byte("foo"), audiences: testAudiences}, - err: errors.Errorf("no x509 certificates found in roots attribute for provisioner foo"), + err: errors.Errorf("no x509 certificates found in roots attribute for provisioner 'foo'"), } }, "fail/invalid-duration": func(t *testing.T) ProvisionerValidateTest { @@ -79,7 +79,7 @@ func TestX5C_Init(t *testing.T) { p.Claims = &Claims{DefaultTLSDur: &Duration{0}} return ProvisionerValidateTest{ p: p, - err: errors.New("claims: DefaultTLSCertDuration must be greater than 0"), + err: errors.New("claims: MinTLSCertDuration must be greater than 0"), } }, "ok": func(t *testing.T) ProvisionerValidateTest { @@ -568,7 +568,7 @@ func TestX5C_AuthorizeRenew(t *testing.T) { return test{ p: p, code: http.StatusUnauthorized, - err: errors.Errorf("x5c.AuthorizeRenew; renew is disabled for x5c provisioner %s", p.GetID()), + err: errors.Errorf("x5c.AuthorizeRenew; renew is disabled for x5c provisioner '%s'", p.GetName()), } }, "ok": func(t *testing.T) test { @@ -624,7 +624,7 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) { p: p, token: "foo", code: http.StatusUnauthorized, - err: errors.Errorf("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner %s", p.GetID()), + err: errors.Errorf("x5c.AuthorizeSSHSign; sshCA is disabled for x5c provisioner '%s'", p.GetName()), } }, "fail/invalid-token": func(t *testing.T) test { diff --git a/authority/provisioners.go b/authority/provisioners.go index 48f67dc8..d2581e76 100644 --- a/authority/provisioners.go +++ b/authority/provisioners.go @@ -1,19 +1,24 @@ package authority import ( + "context" "crypto/x509" "encoding/json" "fmt" - "github.com/smallstep/certificates/authority/mgmt" + "github.com/smallstep/certificates/authority/admin" + "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/linkedca" "go.step.sm/crypto/jose" + "go.step.sm/linkedca" + "gopkg.in/square/go-jose.v2/jwt" ) // GetEncryptedKey returns the JWE key corresponding to the given kid argument. func (a *Authority) GetEncryptedKey(kid string) (string, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() key, ok := a.provisioners.LoadEncryptedKey(kid) if !ok { return "", errs.NotFound("encrypted key with kid %s was not found", kid) @@ -24,6 +29,8 @@ func (a *Authority) GetEncryptedKey(kid string) (string, error) { // GetProvisioners returns a map listing each provisioner and the JWK Key Set // with their public keys. func (a *Authority) GetProvisioners(cursor string, limit int) (provisioner.List, string, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() provisioners, nextCursor := a.provisioners.Find(cursor, limit) return provisioners, nextCursor, nil } @@ -31,39 +38,320 @@ func (a *Authority) GetProvisioners(cursor string, limit int) (provisioner.List, // LoadProvisionerByCertificate returns an interface to the provisioner that // provisioned the certificate. func (a *Authority) LoadProvisionerByCertificate(crt *x509.Certificate) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() p, ok := a.provisioners.LoadByCertificate(crt) if !ok { - return nil, errs.NotFound("provisioner not found") + return nil, admin.NewError(admin.ErrorNotFoundType, "unable to load provisioner from certificate") + } + return p, nil +} + +// LoadProvisionerByToken returns an interface to the provisioner that +// provisioned the token. +func (a *Authority) LoadProvisionerByToken(token *jwt.JSONWebToken, claims *jwt.Claims) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + p, ok := a.provisioners.LoadByToken(token, claims) + if !ok { + return nil, admin.NewError(admin.ErrorNotFoundType, "unable to load provisioner from token") } return p, nil } // LoadProvisionerByID returns an interface to the provisioner with the given ID. func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() p, ok := a.provisioners.Load(id) if !ok { - return nil, errs.NotFound("provisioner not found") + return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", id) } return p, nil } -func provisionerGetOptions(p *linkedca.Provisioner) *provisioner.Options { - return &provisioner.Options{ - X509: &provisioner.X509Options{ - Template: string(p.X509Template), - TemplateData: p.X509TemplateData, +// LoadProvisionerByName returns an interface to the provisioner with the given Name. +func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, error) { + a.adminMutex.RLock() + defer a.adminMutex.RUnlock() + p, ok := a.provisioners.LoadByName(name) + if !ok { + return nil, admin.NewError(admin.ErrorNotFoundType, "provisioner %s not found", name) + } + return p, nil +} + +func (a *Authority) generateProvisionerConfig(ctx context.Context) (*provisioner.Config, error) { + // Merge global and configuration claims + claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims) + if err != nil { + return nil, err + } + // TODO: should we also be combining the ssh federated roots here? + // If we rotate ssh roots keys, sshpop provisioner will lose ability to + // validate old SSH certificates, unless they are added as federated certs. + sshKeys, err := a.GetSSHRoots(ctx) + if err != nil { + return nil, err + } + return &provisioner.Config{ + Claims: claimer.Claims(), + Audiences: a.config.GetAudiences(), + DB: a.db, + SSHKeys: &provisioner.SSHKeys{ + UserKeys: sshKeys.UserKeys, + HostKeys: sshKeys.HostKeys, }, - SSH: &provisioner.SSHOptions{ - Template: string(p.SshTemplate), - TemplateData: p.SshTemplateData, + GetIdentityFunc: a.getIdentityFunc, + }, nil + +} + +// StoreProvisioner stores an provisioner.Interface to the authority. +func (a *Authority) StoreProvisioner(ctx context.Context, prov *linkedca.Provisioner) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + certProv, err := ProvisionerToCertificates(prov) + if err != nil { + return admin.WrapErrorISE(err, + "error converting to certificates provisioner from linkedca provisioner") + } + + if _, ok := a.provisioners.LoadByName(prov.GetName()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with name %s already exists", prov.GetName()) + } + if _, ok := a.provisioners.LoadByTokenID(certProv.GetIDForToken()); ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner with token ID %s already exists", certProv.GetIDForToken()) + } + + // Store to database -- this will set the ID. + if err := a.adminDB.CreateProvisioner(ctx, prov); err != nil { + return admin.WrapErrorISE(err, "error creating admin") + } + + // We need a new conversion that has the newly set ID. + certProv, err = ProvisionerToCertificates(prov) + if err != nil { + return admin.WrapErrorISE(err, + "error converting to certificates provisioner from linkedca provisioner") + } + + provisionerConfig, err := a.generateProvisionerConfig(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error generating provisioner config") + } + + if err := certProv.Init(*provisionerConfig); err != nil { + return admin.WrapErrorISE(err, "error initializing provisioner %s", prov.Name) + } + + if err := a.provisioners.Store(certProv); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed provisioner store") + } + return admin.WrapErrorISE(err, "error storing provisioner in authority cache") + } + return nil +} + +// UpdateProvisioner stores an provisioner.Interface to the authority. +func (a *Authority) UpdateProvisioner(ctx context.Context, nu *linkedca.Provisioner) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + certProv, err := ProvisionerToCertificates(nu) + if err != nil { + return admin.WrapErrorISE(err, + "error converting to certificates provisioner from linkedca provisioner") + } + + provisionerConfig, err := a.generateProvisionerConfig(ctx) + if err != nil { + return admin.WrapErrorISE(err, "error generating provisioner config") + } + + if err := certProv.Init(*provisionerConfig); err != nil { + return admin.WrapErrorISE(err, "error initializing provisioner %s", nu.Name) + } + + if err := a.provisioners.Update(certProv); err != nil { + return admin.WrapErrorISE(err, "error updating provisioner '%s' in authority cache", nu.Name) + } + if err := a.adminDB.UpdateProvisioner(ctx, nu); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed provisioner update") + } + return admin.WrapErrorISE(err, "error updating provisioner '%s'", nu.Name) + } + return nil +} + +// RemoveProvisioner removes an provisioner.Interface from the authority. +func (a *Authority) RemoveProvisioner(ctx context.Context, id string) error { + a.adminMutex.Lock() + defer a.adminMutex.Unlock() + + p, ok := a.provisioners.Load(id) + if !ok { + return admin.NewError(admin.ErrorBadRequestType, + "provisioner %s not found", id) + } + + provName, provID := p.GetName(), p.GetID() + // Validate + // - Check that there will be SUPER_ADMINs that remain after we + // remove this provisioner. + if a.admins.SuperCount() == a.admins.SuperCountByProvisioner(provName) { + return admin.NewError(admin.ErrorBadRequestType, + "cannot remove provisioner %s because no super admins will remain", provName) + } + + // Delete all admins associated with the provisioner. + admins, ok := a.admins.LoadByProvisioner(provName) + if ok { + for _, adm := range admins { + if err := a.removeAdmin(ctx, adm.Id); err != nil { + return admin.WrapErrorISE(err, "error deleting admin %s, as part of provisioner %s deletion", adm.Subject, provName) + } + } + } + + // Remove provisioner from authority caches. + if err := a.provisioners.Remove(provID); err != nil { + return admin.WrapErrorISE(err, "error removing admin from authority cache") + } + // Remove provisioner from database. + if err := a.adminDB.DeleteProvisioner(ctx, provID); err != nil { + if err := a.reloadAdminResources(ctx); err != nil { + return admin.WrapErrorISE(err, "error reloading admin resources on failed provisioner remove") + } + return admin.WrapErrorISE(err, "error deleting provisioner %s", provName) + } + return nil +} + +func CreateFirstProvisioner(ctx context.Context, db admin.DB, password string) (*linkedca.Provisioner, error) { + jwk, jwe, err := jose.GenerateDefaultKeyPair([]byte(password)) + if err != nil { + return nil, admin.WrapErrorISE(err, "error generating JWK key pair") + } + + jwkPubBytes, err := jwk.MarshalJSON() + if err != nil { + return nil, admin.WrapErrorISE(err, "error marshaling JWK") + } + jwePrivStr, err := jwe.CompactSerialize() + if err != nil { + return nil, admin.WrapErrorISE(err, "error serializing JWE") + } + + p := &linkedca.Provisioner{ + Name: "Admin JWK", + Type: linkedca.Provisioner_JWK, + Details: &linkedca.ProvisionerDetails{ + Data: &linkedca.ProvisionerDetails_JWK{ + JWK: &linkedca.JWKProvisioner{ + PublicKey: jwkPubBytes, + EncryptedPrivateKey: []byte(jwePrivStr), + }, + }, + }, + Claims: &linkedca.Claims{ + X509: &linkedca.X509Claims{ + Enabled: true, + Durations: &linkedca.Durations{ + Default: "5m", + }, + }, }, } + if err := db.CreateProvisioner(ctx, p); err != nil { + return nil, admin.WrapErrorISE(err, "error creating provisioner") + } + return p, nil +} + +func ValidateClaims(c *linkedca.Claims) error { + if c == nil { + return nil + } + if c.X509 != nil { + if c.X509.Durations != nil { + if err := ValidateDurations(c.X509.Durations); err != nil { + return err + } + } + } + if c.Ssh != nil { + if c.Ssh.UserDurations != nil { + if err := ValidateDurations(c.Ssh.UserDurations); err != nil { + return err + } + } + if c.Ssh.HostDurations != nil { + if err := ValidateDurations(c.Ssh.HostDurations); err != nil { + return err + } + } + } + return nil +} + +func ValidateDurations(d *linkedca.Durations) error { + var ( + err error + min, max, def *provisioner.Duration + ) + + if d.Min != "" { + min, err = provisioner.NewDuration(d.Min) + if err != nil { + return admin.WrapError(admin.ErrorBadRequestType, err, "min duration '%s' is invalid", d.Min) + } + if min.Value() < 0 { + return admin.WrapError(admin.ErrorBadRequestType, err, "min duration '%s' cannot be less than 0", d.Min) + } + } + if d.Max != "" { + max, err = provisioner.NewDuration(d.Max) + if err != nil { + return admin.WrapError(admin.ErrorBadRequestType, err, "max duration '%s' is invalid", d.Max) + } + if max.Value() < 0 { + return admin.WrapError(admin.ErrorBadRequestType, err, "max duration '%s' cannot be less than 0", d.Max) + } + } + if d.Default != "" { + def, err = provisioner.NewDuration(d.Default) + if err != nil { + return admin.WrapError(admin.ErrorBadRequestType, err, "default duration '%s' is invalid", d.Default) + } + if def.Value() < 0 { + return admin.WrapError(admin.ErrorBadRequestType, err, "default duration '%s' cannot be less than 0", d.Default) + } + } + if d.Min != "" && d.Max != "" && min.Value() > max.Value() { + return admin.NewError(admin.ErrorBadRequestType, + "min duration '%s' cannot be greater than max duration '%s'", d.Min, d.Max) + } + if d.Min != "" && d.Default != "" && min.Value() > def.Value() { + return admin.NewError(admin.ErrorBadRequestType, + "min duration '%s' cannot be greater than default duration '%s'", d.Min, d.Default) + } + if d.Default != "" && d.Max != "" && min.Value() > def.Value() { + return admin.NewError(admin.ErrorBadRequestType, + "default duration '%s' cannot be greater than max duration '%s'", d.Default, d.Max) + } + return nil } func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List, error) { var nu provisioner.List for _, p := range l { - certProv, err := provisionerToCertificates(p) + certProv, err := ProvisionerToCertificates(p) if err != nil { return nil, err } @@ -72,9 +360,87 @@ func provisionerListToCertificates(l []*linkedca.Provisioner) (provisioner.List, return nu, nil } -// provisionerToCertificates converts the landlord provisioner type to the open source -// provisioner type. -func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { +func optionsToCertificates(p *linkedca.Provisioner) *provisioner.Options { + ops := &provisioner.Options{ + X509: &provisioner.X509Options{}, + SSH: &provisioner.SSHOptions{}, + } + if p.X509Template != nil { + ops.X509.Template = string(p.X509Template.Template) + ops.X509.TemplateData = p.X509Template.Data + } + if p.SshTemplate != nil { + ops.SSH.Template = string(p.SshTemplate.Template) + ops.SSH.TemplateData = p.SshTemplate.Data + } + return ops +} + +func durationsToCertificates(d *linkedca.Durations) (min, max, def *provisioner.Duration, err error) { + if len(d.Min) > 0 { + min, err = provisioner.NewDuration(d.Min) + if err != nil { + return nil, nil, nil, admin.WrapErrorISE(err, "error parsing minimum duration '%s'", d.Min) + } + } + if len(d.Max) > 0 { + max, err = provisioner.NewDuration(d.Max) + if err != nil { + return nil, nil, nil, admin.WrapErrorISE(err, "error parsing maximum duration '%s'", d.Max) + } + } + if len(d.Default) > 0 { + def, err = provisioner.NewDuration(d.Default) + if err != nil { + return nil, nil, nil, admin.WrapErrorISE(err, "error parsing default duration '%s'", d.Default) + } + } + return +} + +// claimsToCertificates converts the linkedca provisioner claims type to the +// certifictes claims type. +func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { + if c == nil { + return nil, nil + } + + pc := &provisioner.Claims{ + DisableRenewal: &c.DisableRenewal, + } + + var err error + + if xc := c.X509; xc != nil { + if d := xc.Durations; d != nil { + pc.MinTLSDur, pc.MaxTLSDur, pc.DefaultTLSDur, err = durationsToCertificates(d) + if err != nil { + return nil, err + } + } + } + if sc := c.Ssh; sc != nil { + pc.EnableSSHCA = &sc.Enabled + if d := sc.UserDurations; d != nil { + pc.MinUserSSHDur, pc.MaxUserSSHDur, pc.DefaultUserSSHDur, err = durationsToCertificates(d) + if err != nil { + return nil, err + } + } + if d := sc.HostDurations; d != nil { + pc.MinHostSSHDur, pc.MaxHostSSHDur, pc.DefaultHostSSHDur, err = durationsToCertificates(d) + if err != nil { + return nil, err + } + } + } + + return pc, nil +} + +// ProvisionerToCertificates converts the linkedca provisioner type to the certificates provisioner +// interface. +func ProvisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, error) { claims, err := claimsToCertificates(p.Claims) if err != nil { return nil, err @@ -85,9 +451,10 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, return nil, fmt.Errorf("provisioner does not have any details") } + options := optionsToCertificates(p) + switch d := details.(type) { case *linkedca.ProvisionerDetails_JWK: - fmt.Printf("d = %+v\n", d) jwk := new(jose.JSONWebKey) if err := json.Unmarshal(d.JWK.PublicKey, &jwk); err != nil { return nil, err @@ -99,153 +466,136 @@ func provisionerToCertificates(p *linkedca.Provisioner) (provisioner.Interface, Key: jwk, EncryptedKey: string(d.JWK.EncryptedPrivateKey), Claims: claims, - Options: provisionerGetOptions(p), + Options: options, + }, nil + case *linkedca.ProvisionerDetails_X5C: + var roots []byte + for i, root := range d.X5C.GetRoots() { + if i > 0 { + roots = append(roots, '\n') + } + roots = append(roots, root...) + } + return &provisioner.X5C{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Roots: roots, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_K8SSA: + var publicKeys []byte + for i, k := range d.K8SSA.GetPublicKeys() { + if i > 0 { + publicKeys = append(publicKeys, '\n') + } + publicKeys = append(publicKeys, k...) + } + return &provisioner.K8sSA{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + PubKeys: publicKeys, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_SSHPOP: + return &provisioner.SSHPOP{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Claims: claims, + }, nil + case *linkedca.ProvisionerDetails_ACME: + cfg := d.ACME + return &provisioner.ACME{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + ForceCN: cfg.ForceCn, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_OIDC: + cfg := d.OIDC + return &provisioner.OIDC{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ClientID: cfg.ClientId, + ClientSecret: cfg.ClientSecret, + ConfigurationEndpoint: cfg.ConfigurationEndpoint, + Admins: cfg.Admins, + Domains: cfg.Domains, + Groups: cfg.Groups, + ListenAddress: cfg.ListenAddress, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_AWS: + cfg := d.AWS + instanceAge, err := parseInstanceAge(cfg.InstanceAge) + if err != nil { + return nil, err + } + return &provisioner.AWS{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + Accounts: cfg.Accounts, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: instanceAge, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_GCP: + cfg := d.GCP + instanceAge, err := parseInstanceAge(cfg.InstanceAge) + if err != nil { + return nil, err + } + return &provisioner.GCP{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + ServiceAccounts: cfg.ServiceAccounts, + ProjectIDs: cfg.ProjectIds, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + InstanceAge: instanceAge, + Claims: claims, + Options: options, + }, nil + case *linkedca.ProvisionerDetails_Azure: + cfg := d.Azure + return &provisioner.Azure{ + ID: p.Id, + Type: p.Type.String(), + Name: p.Name, + TenantID: cfg.TenantId, + ResourceGroups: cfg.ResourceGroups, + Audience: cfg.Audience, + DisableCustomSANs: cfg.DisableCustomSans, + DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, + Claims: claims, + Options: options, }, nil - /* - case *ProvisionerDetails_OIDC: - cfg := d.OIDC - return &provisioner.OIDC{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ClientID: cfg.ClientId, - ClientSecret: cfg.ClientSecret, - ConfigurationEndpoint: cfg.ConfigurationEndpoint, - Admins: cfg.Admins, - Domains: cfg.Domains, - Groups: cfg.Groups, - ListenAddress: cfg.ListenAddress, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_GCP: - cfg := d.GCP - return &provisioner.GCP{ - Type: p.Type.String(), - Name: p.Name, - ServiceAccounts: cfg.ServiceAccounts, - ProjectIDs: cfg.ProjectIds, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_AWS: - cfg := d.AWS - return &provisioner.AWS{ - Type: p.Type.String(), - Name: p.Name, - Accounts: cfg.Accounts, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - InstanceAge: durationValue(cfg.InstanceAge), - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_Azure: - cfg := d.Azure - return &provisioner.Azure{ - Type: p.Type.String(), - Name: p.Name, - TenantID: cfg.TenantId, - ResourceGroups: cfg.ResourceGroups, - Audience: cfg.Audience, - DisableCustomSANs: cfg.DisableCustomSans, - DisableTrustOnFirstUse: cfg.DisableTrustOnFirstUse, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_X5C: - var roots []byte - for i, k := range d.X5C.GetRoots() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - roots = append(roots, '\n') - } - roots = append(roots, b...) - } - } - return &provisioner.X5C{ - Type: p.Type.String(), - Name: p.Name, - Roots: roots, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_K8SSA: - var publicKeys []byte - for i, k := range d.K8SSA.GetPublicKeys() { - if b := k.GetKey().GetPublic(); b != nil { - if i > 0 { - publicKeys = append(publicKeys, '\n') - } - publicKeys = append(publicKeys, k.Key.Public...) - } - } - return &provisioner.K8sSA{ - Type: p.Type.String(), - Name: p.Name, - PubKeys: publicKeys, - Claims: claims, - Options: options, - }, nil - case *ProvisionerDetails_SSHPOP: - return &provisioner.SSHPOP{ - Type: p.Type.String(), - Name: p.Name, - Claims: claims, - }, nil - case *ProvisionerDetails_ACME: - cfg := d.ACME - return &provisioner.ACME{ - Type: p.Type.String(), - Name: p.Name, - ForceCN: cfg.ForceCn, - Claims: claims, - Options: options, - }, nil - */ default: return nil, fmt.Errorf("provisioner %s not implemented", p.Type) } } -// claimsToCertificates converts the landlord provisioner claims type to the open source -// (step-ca) claims type. -func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) { - var durs = map[string]struct { - durStr string - dur *provisioner.Duration - }{ - "minTLSDur": {durStr: c.X509.Durations.Min}, - "maxTLSDur": {durStr: c.X509.Durations.Max}, - "defaultTLSDur": {durStr: c.X509.Durations.Default}, - "minSSHUserDur": {durStr: c.Ssh.UserDurations.Min}, - "maxSSHUserDur": {durStr: c.Ssh.UserDurations.Max}, - "defaultSSHUserDur": {durStr: c.Ssh.UserDurations.Default}, - "minSSHHostDur": {durStr: c.Ssh.HostDurations.Min}, - "maxSSHHostDur": {durStr: c.Ssh.HostDurations.Max}, - "defaultSSHHostDur": {durStr: c.Ssh.HostDurations.Default}, - } - var err error - for k, v := range durs { - v.dur, err = provisioner.NewDuration(v.durStr) +func parseInstanceAge(age string) (provisioner.Duration, error) { + var instanceAge provisioner.Duration + if age != "" { + iap, err := provisioner.NewDuration(age) if err != nil { - return nil, mgmt.WrapErrorISE(err, "error parsing %s %s from claims", k, v.durStr) - } - } - return &provisioner.Claims{ - MinTLSDur: durs["minTLSDur"].dur, - MaxTLSDur: durs["maxTLSDur"].dur, - DefaultTLSDur: durs["defaultTLSDur"].dur, - DisableRenewal: &c.DisableRenewal, - MinUserSSHDur: durs["minSSHUserDur"].dur, - MaxUserSSHDur: durs["maxSSHUserDur"].dur, - DefaultUserSSHDur: durs["defaultSSHUserDur"].dur, - MinHostSSHDur: durs["minSSHHostDur"].dur, - MaxHostSSHDur: durs["maxSSHHostDur"].dur, - DefaultHostSSHDur: durs["defaultSSHHostDur"].dur, - EnableSSHCA: &c.Ssh.Enabled, - }, nil + return instanceAge, err + } + instanceAge = *iap + } + return instanceAge, nil } diff --git a/authority/provisioners_test.go b/authority/provisioners_test.go index 94b2d715..3975031b 100644 --- a/authority/provisioners_test.go +++ b/authority/provisioners_test.go @@ -56,7 +56,7 @@ func TestGetEncryptedKey(t *testing.T) { } } else { if assert.Nil(t, tc.err) { - val, ok := tc.a.provisioners.Load("max:" + tc.kid) + val, ok := tc.a.provisioners.Load("mike:" + tc.kid) assert.Fatal(t, ok) p, ok := val.(*provisioner.JWK) assert.Fatal(t, ok) diff --git a/authority/ssh_test.go b/authority/ssh_test.go index 1662260c..8ca26af0 100644 --- a/authority/ssh_test.go +++ b/authority/ssh_test.go @@ -590,69 +590,6 @@ func TestSSHConfig_Validate(t *testing.T) { } } -func TestSSHPublicKey_Validate(t *testing.T) { - key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) - assert.FatalError(t, err) - - type fields struct { - Type string - Federated bool - Key jose.JSONWebKey - } - tests := []struct { - name string - fields fields - wantErr bool - }{ - {"user", fields{"user", true, key.Public()}, false}, - {"host", fields{"host", false, key.Public()}, false}, - {"empty", fields{"", true, key.Public()}, true}, - {"badType", fields{"bad", false, key.Public()}, true}, - {"badKey", fields{"user", false, *key}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - k := &SSHPublicKey{ - Type: tt.fields.Type, - Federated: tt.fields.Federated, - Key: tt.fields.Key, - } - if err := k.Validate(); (err != nil) != tt.wantErr { - t.Errorf("SSHPublicKey.Validate() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestSSHPublicKey_PublicKey(t *testing.T) { - key, err := jose.GenerateJWK("EC", "P-256", "", "sig", "", 0) - assert.FatalError(t, err) - pub, err := ssh.NewPublicKey(key.Public().Key) - assert.FatalError(t, err) - - type fields struct { - publicKey ssh.PublicKey - } - tests := []struct { - name string - fields fields - want ssh.PublicKey - }{ - {"ok", fields{pub}, pub}, - {"nil", fields{nil}, nil}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - k := &SSHPublicKey{ - publicKey: tt.fields.publicKey, - } - if got := k.PublicKey(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("SSHPublicKey.PublicKey() = %v, want %v", got, tt.want) - } - }) - } -} - func TestAuthority_GetSSHBastion(t *testing.T) { bastion := &Bastion{ Hostname: "bastion.local", diff --git a/authority/tls.go b/authority/tls.go index 1c8b3b8c..4c3420df 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -360,10 +360,9 @@ func (a *Authority) Revoke(ctx context.Context, revokeOpts *RevokeOptions) error } // This method will also validate the audiences for JWK provisioners. - var ok bool - p, ok = a.provisioners.LoadByToken(token, &claims.Claims) - if !ok { - return errs.InternalServer("authority.Revoke; provisioner not found", opts...) + p, err = a.LoadProvisionerByToken(token, &claims.Claims) + if err != nil { + return err } rci.ProvisionerID = p.GetID() rci.TokenID, err = p.GetTokenID(revokeOpts.OTT) diff --git a/authority/tls_test.go b/authority/tls_test.go index 4c936f0c..cdd4c59a 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -656,7 +656,7 @@ func TestAuthority_Renew(t *testing.T) { "fail/unauthorized": func() (*renewTest, error) { return &renewTest{ cert: certNoRenew, - err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner dev:IMi94WBNI6gP5cNHXlZYNUzvMjGdHyBRmFoo-lCEaqk"), + err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner 'dev'"), code: http.StatusUnauthorized, }, nil }, @@ -856,7 +856,7 @@ func TestAuthority_Rekey(t *testing.T) { "fail/unauthorized": func() (*renewTest, error) { return &renewTest{ cert: certNoRenew, - err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner dev:IMi94WBNI6gP5cNHXlZYNUzvMjGdHyBRmFoo-lCEaqk"), + err: errors.New("authority.Rekey: authority.authorizeRenew: jwk.AuthorizeRenew; renew is disabled for jwk provisioner 'dev'"), code: http.StatusUnauthorized, }, nil }, diff --git a/ca/adminClient.go b/ca/adminClient.go index ad708146..da2ff566 100644 --- a/ca/adminClient.go +++ b/ca/adminClient.go @@ -2,29 +2,42 @@ package ca import ( "bytes" + "crypto/x509" "encoding/json" "io" "net/http" "net/url" "path" "strconv" + "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/authority/mgmt" - mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" + "github.com/smallstep/certificates/authority/admin" + adminAPI "github.com/smallstep/certificates/authority/admin/api" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/linkedca" + "go.step.sm/cli-utils/token" + "go.step.sm/cli-utils/token/provision" + "go.step.sm/crypto/jose" + "go.step.sm/crypto/randutil" + "go.step.sm/linkedca" + "google.golang.org/protobuf/encoding/protojson" ) var adminURLPrefix = "admin" // AdminClient implements an HTTP client for the CA server. type AdminClient struct { - client *uaClient - endpoint *url.URL - retryFunc RetryFunc - opts []ClientOption + client *uaClient + endpoint *url.URL + retryFunc RetryFunc + opts []ClientOption + x5cJWK *jose.JSONWebKey + x5cCertFile string + x5cCertStrs []string + x5cCert *x509.Certificate + x5cIssuer string + x5cSubject string } // NewAdminClient creates a new AdminClient with the given endpoint and options. @@ -44,13 +57,45 @@ func NewAdminClient(endpoint string, opts ...ClientOption) (*AdminClient, error) } return &AdminClient{ - client: newClient(tr), - endpoint: u, - retryFunc: o.retryFunc, - opts: opts, + client: newClient(tr), + endpoint: u, + retryFunc: o.retryFunc, + opts: opts, + x5cJWK: o.x5cJWK, + x5cCertFile: o.x5cCertFile, + x5cCertStrs: o.x5cCertStrs, + x5cCert: o.x5cCert, + x5cIssuer: o.x5cIssuer, + x5cSubject: o.x5cSubject, }, nil } +func (c *AdminClient) generateAdminToken(path string) (string, error) { + // A random jwt id will be used to identify duplicated tokens + jwtID, err := randutil.Hex(64) // 256 bits + if err != nil { + return "", err + } + + now := time.Now() + tokOptions := []token.Options{ + token.WithJWTID(jwtID), + token.WithKid(c.x5cJWK.KeyID), + token.WithIssuer(c.x5cIssuer), + token.WithAudience(path), + token.WithValidity(now, now.Add(token.DefaultValidity)), + token.WithX5CCerts(c.x5cCertStrs), + } + + tok, err := provision.New(c.x5cSubject, tokOptions...) + if err != nil { + return "", err + } + + return tok.SignedString(c.x5cJWK.Algorithm, c.x5cJWK.Key) + +} + func (c *AdminClient) retryOnError(r *http.Response) bool { if c.retryFunc != nil { if c.retryFunc(r.StatusCode) { @@ -138,7 +183,7 @@ func WithAdminLimit(limit int) AdminOption { } // GetAdminsPaginate returns a page from the the GET /admin/admins request to the CA. -func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*mgmtAPI.GetAdminsResponse, error) { +func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*adminAPI.GetAdminsResponse, error) { var retried bool o := new(adminOptions) if err := o.apply(opts); err != nil { @@ -148,8 +193,17 @@ func (c *AdminClient) GetAdminsPaginate(opts ...AdminOption) (*mgmtAPI.GetAdmins Path: "/admin/admins", RawQuery: o.rawQuery(), }) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create GET %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", u) } @@ -160,7 +214,7 @@ retry: } return nil, readAdminError(resp.Body) } - var body = new(mgmtAPI.GetAdminsResponse) + var body = new(adminAPI.GetAdminsResponse) if err := readJSON(resp.Body, body); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -184,19 +238,27 @@ func (c *AdminClient) GetAdmins(opts ...AdminOption) ([]*linkedca.Admin, error) } cursor = resp.NextCursor } - return admins, nil } // CreateAdmin performs the POST /admin/admins request to the CA. -func (c *AdminClient) CreateAdmin(req *mgmtAPI.CreateAdminRequest) (*linkedca.Admin, error) { +func (c *AdminClient) CreateAdmin(createAdminRequest *adminAPI.CreateAdminRequest) (*linkedca.Admin, error) { var retried bool - body, err := json.Marshal(req) + body, err := json.Marshal(createAdminRequest) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/admins"}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "create GET %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client POST %s failed", u) } @@ -218,10 +280,15 @@ retry: func (c *AdminClient) RemoveAdmin(id string) error { var retried bool u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "admins", id)}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return errors.Wrapf(err, "error generating admin token") + } req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { @@ -238,17 +305,22 @@ retry: } // UpdateAdmin performs the PUT /admin/admins/{id} request to the CA. -func (c *AdminClient) UpdateAdmin(id string, uar *mgmtAPI.UpdateAdminRequest) (*linkedca.Admin, error) { +func (c *AdminClient) UpdateAdmin(id string, uar *adminAPI.UpdateAdminRequest) (*linkedca.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(adminURLPrefix, "admins", id)}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(body)) if err != nil { return nil, errors.Wrapf(err, "create PUT %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { @@ -268,12 +340,35 @@ retry: return adm, nil } -// GetProvisionerByName performs the GET /admin/provisioners/{name} request to the CA. -func (c *AdminClient) GetProvisionerByName(name string) (*linkedca.Provisioner, error) { +// GetProvisioner performs the GET /admin/provisioners/{name} request to the CA. +func (c *AdminClient) GetProvisioner(opts ...ProvisionerOption) (*linkedca.Provisioner, error) { var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) + o := new(provisionerOptions) + if err := o.apply(opts); err != nil { + return nil, err + } + var u *url.URL + if len(o.id) > 0 { + u = c.endpoint.ResolveReference(&url.URL{ + Path: "/admin/provisioners/id", + RawQuery: o.rawQuery(), + }) + } else if len(o.name) > 0 { + u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) + } else { + return nil, errors.New("must set either name or id in method options") + } + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create PUT %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", u) } @@ -285,14 +380,14 @@ retry: return nil, readAdminError(resp.Body) } var prov = new(linkedca.Provisioner) - if err := readJSON(resp.Body, prov); err != nil { + if err := readProtoJSON(resp.Body, prov); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return prov, nil } // GetProvisionersPaginate performs the GET /admin/provisioners request to the CA. -func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*mgmtAPI.GetProvisionersResponse, error) { +func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*adminAPI.GetProvisionersResponse, error) { var retried bool o := new(provisionerOptions) if err := o.apply(opts); err != nil { @@ -302,8 +397,17 @@ func (c *AdminClient) GetProvisionersPaginate(opts ...ProvisionerOption) (*mgmtA Path: "/admin/provisioners", RawQuery: o.rawQuery(), }) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, errors.Wrapf(err, "create PUT %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Get(u.String()) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client GET %s failed", u) } @@ -314,7 +418,7 @@ retry: } return nil, readAdminError(resp.Body) } - var body = new(mgmtAPI.GetProvisionersResponse) + var body = new(adminAPI.GetProvisionersResponse) if err := readJSON(resp.Body, body); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } @@ -338,17 +442,39 @@ func (c *AdminClient) GetProvisioners(opts ...AdminOption) (provisioner.List, er } cursor = resp.NextCursor } - return provs, nil } // RemoveProvisioner performs the DELETE /admin/provisioners/{name} request to the CA. -func (c *AdminClient) RemoveProvisioner(name string) error { - var retried bool - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) +func (c *AdminClient) RemoveProvisioner(opts ...ProvisionerOption) error { + var ( + u *url.URL + retried bool + ) + + o := new(provisionerOptions) + if err := o.apply(opts); err != nil { + return err + } + + if len(o.id) > 0 { + u = c.endpoint.ResolveReference(&url.URL{ + Path: path.Join(adminURLPrefix, "provisioners/id"), + RawQuery: o.rawQuery(), + }) + } else if len(o.name) > 0 { + u = c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", o.name)}) + } else { + return errors.New("must set either name or id in method options") + } + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return errors.Wrapf(err, "error generating admin token") + } req, err := http.NewRequest("DELETE", u.String(), nil) if err != nil { return errors.Wrapf(err, "create DELETE %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { @@ -367,13 +493,22 @@ retry: // CreateProvisioner performs the POST /admin/provisioners request to the CA. func (c *AdminClient) CreateProvisioner(prov *linkedca.Provisioner) (*linkedca.Provisioner, error) { var retried bool - body, err := json.Marshal(prov) + body, err := protojson.Marshal(prov) if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") } - u := c.endpoint.ResolveReference(&url.URL{Path: "/admin/provisioners"}) + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners")}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return nil, errors.Wrapf(err, "error generating admin token") + } + req, err := http.NewRequest("POST", u.String(), bytes.NewReader(body)) + if err != nil { + return nil, errors.Wrapf(err, "create POST %s request failed", u) + } + req.Header.Add("Authorization", tok) retry: - resp, err := c.client.Post(u.String(), "application/json", bytes.NewReader(body)) + resp, err := c.client.Do(req) if err != nil { return nil, errors.Wrapf(err, "client POST %s failed", u) } @@ -385,48 +520,49 @@ retry: return nil, readAdminError(resp.Body) } var nuProv = new(linkedca.Provisioner) - if err := readJSON(resp.Body, nuProv); err != nil { + if err := readProtoJSON(resp.Body, nuProv); err != nil { return nil, errors.Wrapf(err, "error reading %s", u) } return nuProv, nil } -// UpdateProvisioner performs the PUT /admin/provisioners/{id} request to the CA. -func (c *AdminClient) UpdateProvisioner(id string, upr *mgmtAPI.UpdateProvisionerRequest) (*linkedca.Provisioner, error) { +// UpdateProvisioner performs the PUT /admin/provisioners/{name} request to the CA. +func (c *AdminClient) UpdateProvisioner(name string, prov *linkedca.Provisioner) error { var retried bool - body, err := json.Marshal(upr) + body, err := protojson.Marshal(prov) if err != nil { - return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + return errs.Wrap(http.StatusInternalServerError, err, "error marshaling request") + } + u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", name)}) + tok, err := c.generateAdminToken(u.Path) + if err != nil { + return errors.Wrapf(err, "error generating admin token") } - u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "provisioners", 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) + return errors.Wrapf(err, "create PUT %s request failed", u) } + req.Header.Add("Authorization", tok) retry: resp, err := c.client.Do(req) if err != nil { - return nil, errors.Wrapf(err, "client PUT %s failed", u) + return errors.Wrapf(err, "client PUT %s failed", u) } if resp.StatusCode >= 400 { if !retried && c.retryOnError(resp) { retried = true goto retry } - return nil, readAdminError(resp.Body) - } - var prov = new(linkedca.Provisioner) - if err := readJSON(resp.Body, prov); err != nil { - return nil, errors.Wrapf(err, "error reading %s", u) + return readAdminError(resp.Body) } - return prov, nil + return nil } func readAdminError(r io.ReadCloser) error { defer r.Close() - mgmtErr := new(mgmt.Error) - if err := json.NewDecoder(r).Decode(mgmtErr); err != nil { + adminErr := new(admin.Error) + if err := json.NewDecoder(r).Decode(adminErr); err != nil { return err } - return errors.New(mgmtErr.Message) + return errors.New(adminErr.Message) } diff --git a/ca/ca.go b/ca/ca.go index 1882e8a6..1ff265bf 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -17,9 +17,8 @@ import ( acmeNoSQL "github.com/smallstep/certificates/acme/db/nosql" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority" + adminAPI "github.com/smallstep/certificates/authority/admin/api" "github.com/smallstep/certificates/authority/config" - "github.com/smallstep/certificates/authority/mgmt" - mgmtAPI "github.com/smallstep/certificates/authority/mgmt/api" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/monitoring" @@ -79,11 +78,12 @@ func WithDatabase(db db.AuthDB) Option { // CA is the type used to build the complete certificate authority. It builds // the HTTP server, set ups the middlewares and the HTTP handlers. type CA struct { - auth *authority.Authority - config *config.Config - srv *server.Server - opts *options - renewer *TLSRenewer + auth *authority.Authority + config *config.Config + srv *server.Server + insecureSrv *server.Server + opts *options + renewer *TLSRenewer } // New creates and initializes the CA with the given configuration and options. @@ -130,6 +130,9 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { mux := chi.NewRouter() handler := http.Handler(mux) + insecureMux := chi.NewRouter() + insecureHandler := http.Handler(insecureMux) + // Add regular CA api endpoints in / and /1.0 routerHandler := api.New(auth) routerHandler.Route(mux) @@ -154,7 +157,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { if config.DB == nil { acmeDB = nil } else { - acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB), mgmt.DefaultAuthorityID) + acmeDB, err = acmeNoSQL.New(auth.GetDatabase().(nosql.DB)) if err != nil { return nil, errors.Wrap(err, "error configuring ACME DB interface") } @@ -176,12 +179,14 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) // Admin API Router - adminDB := auth.GetAdminDatabase() - if adminDB != nil { - mgmtHandler := mgmtAPI.NewHandler(auth) - mux.Route("/admin", func(r chi.Router) { - mgmtHandler.Route(r) - }) + if config.AuthorityConfig.EnableAdmin { + adminDB := auth.GetAdminDatabase() + if adminDB != nil { + adminHandler := adminAPI.NewHandler(auth) + mux.Route("/admin", func(r chi.Router) { + adminHandler.Route(r) + }) + } } if ca.shouldServeSCEPEndpoints() { diff --git a/ca/client.go b/ca/client.go index 3a3350ac..8997fbd5 100644 --- a/ca/client.go +++ b/ca/client.go @@ -10,8 +10,10 @@ import ( "crypto/tls" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/hex" "encoding/json" + "encoding/pem" "io" "io/ioutil" "net/http" @@ -28,10 +30,13 @@ import ( "github.com/smallstep/certificates/ca/identity" "github.com/smallstep/certificates/errs" "go.step.sm/cli-utils/config" + "go.step.sm/crypto/jose" "go.step.sm/crypto/keyutil" "go.step.sm/crypto/pemutil" "go.step.sm/crypto/x509util" "golang.org/x/net/http2" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" "gopkg.in/square/go-jose.v2/jwt" ) @@ -108,6 +113,12 @@ type clientOptions struct { certificate tls.Certificate getClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error) retryFunc RetryFunc + x5cJWK *jose.JSONWebKey + x5cCertFile string + x5cCertStrs []string + x5cCert *x509.Certificate + x5cIssuer string + x5cSubject string } func (o *clientOptions) apply(opts []ClientOption) (err error) { @@ -266,9 +277,66 @@ func WithCABundle(bundle []byte) ClientOption { // WithCertificate will set the given certificate as the TLS client certificate // in the client. -func WithCertificate(crt tls.Certificate) ClientOption { +func WithCertificate(cert tls.Certificate) ClientOption { return func(o *clientOptions) error { - o.certificate = crt + o.certificate = cert + return nil + } +} + +var ( + stepOIDRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64} + stepOIDProvisioner = append(asn1.ObjectIdentifier(nil), append(stepOIDRoot, 1)...) +) + +type stepProvisionerASN1 struct { + Type int + Name []byte + CredentialID []byte + KeyValuePairs []string `asn1:"optional,omitempty"` +} + +// WithAdminX5C will set the given file as the X5C certificate for use +// by the client. +func WithAdminX5C(certs []*x509.Certificate, key interface{}, passwordFile string) ClientOption { + return func(o *clientOptions) error { + // Get private key from given key file + var ( + err error + opts []jose.Option + ) + if len(passwordFile) != 0 { + opts = append(opts, jose.WithPasswordFile(passwordFile)) + } + blk, err := pemutil.Serialize(key) + if err != nil { + return errors.Wrap(err, "error serializing private key") + } + o.x5cJWK, err = jose.ParseKey(pem.EncodeToMemory(blk), opts...) + if err != nil { + return err + } + o.x5cCertStrs, err = jose.ValidateX5C(certs, o.x5cJWK.Key) + if err != nil { + return errors.Wrap(err, "error validating x5c certificate chain and key for use in x5c header") + } + + o.x5cCert = certs[0] + o.x5cSubject = o.x5cCert.Subject.CommonName + + for _, e := range o.x5cCert.Extensions { + if e.Id.Equal(stepOIDProvisioner) { + var provisioner stepProvisionerASN1 + if _, err := asn1.Unmarshal(e.Value, &provisioner); err != nil { + return errors.Wrap(err, "error unmarshaling provisioner OID from certificate") + } + o.x5cIssuer = string(provisioner.Name) + } + } + if len(o.x5cIssuer) == 0 { + return errors.New("provisioner extension not found in certificate") + } + return nil } } @@ -372,6 +440,8 @@ type ProvisionerOption func(o *provisionerOptions) error type provisionerOptions struct { cursor string limit int + id string + name string } func (o *provisionerOptions) apply(opts []ProvisionerOption) (err error) { @@ -391,6 +461,12 @@ func (o *provisionerOptions) rawQuery() string { if o.limit > 0 { v.Set("limit", strconv.Itoa(o.limit)) } + if len(o.id) > 0 { + v.Set("id", o.id) + } + if len(o.name) > 0 { + v.Set("name", o.name) + } return v.Encode() } @@ -410,6 +486,22 @@ func WithProvisionerLimit(limit int) ProvisionerOption { } } +// WithProvisionerID will request the given provisioner. +func WithProvisionerID(id string) ProvisionerOption { + return func(o *provisionerOptions) error { + o.id = id + return nil + } +} + +// WithProvisionerName will request the given provisioner. +func WithProvisionerName(name string) ProvisionerOption { + return func(o *provisionerOptions) error { + o.name = name + return nil + } +} + // Client implements an HTTP client for the CA server. type Client struct { client *uaClient @@ -1211,6 +1303,15 @@ func readJSON(r io.ReadCloser, v interface{}) error { return json.NewDecoder(r).Decode(v) } +func readProtoJSON(r io.ReadCloser, m proto.Message) error { + defer r.Close() + data, err := ioutil.ReadAll(r) + if err != nil { + return err + } + return protojson.Unmarshal(data, m) +} + func readError(r io.ReadCloser) error { defer r.Close() apiErr := new(errs.Error) diff --git a/ca/testdata/ca.json b/ca/testdata/ca.json index b094c02e..0a5149d9 100644 --- a/ca/testdata/ca.json +++ b/ca/testdata/ca.json @@ -34,7 +34,7 @@ "y": "ZhYcFQBqtErdC_pA7sOXrO7AboCEPIKP9Ik4CHJqANk" } }, { - "name": "max", + "name": "mike", "type": "jwk", "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlZsWnl0dUxrWTR5enlqZXJybnN0aGcifQ.QP15wQYjZ12BLgl-XTq2Vb12G3OHAfic.X35QqAaXwnlmeCUU._2qIUp0TI8yDI7c2e9upIRdrnmB5OvtLfrYN-Su2NLBpaoYtr9O55Wo0Iryc0W2pYqnVDPvgPPes4P4nQAnzw5WhFYc1Xf1ZEetfdNhwi1x2FNwPbACBAgxm5AW40O5AAlbLcWushYASfeMBZocTGXuSGUzwFqoWD-5EDJ80TWQ7cAj3ttHrJ_3QV9hi4O9KJUCiXngN-Yz2zXrhBL4NOH2fmRbaf5c0rF8xUJIIW-TcyYJeX_Fbx1IzzKKPd9USUwkDhxD4tLa51I345xVqjuwG1PEn6nF8JKqLRVUKEKFin-ShXrfE61KceyAvm4YhWKrbJWIm3bH5Hxaphy4.TexIrIhsRxJStpE3EJ925Q", "key": { @@ -76,7 +76,7 @@ "minTLSCertDuration": "1s" } }, { - "name": "mariano", + "name": "maxey", "type": "jwk", "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6Ik5SLTk5ZkVMSm1CLW1FZGllUlFFc3cifQ.Fr314BEUGTda4ICJl2uxFdjpEUGGqJEV.gBbu_DZE1ONDu14r.X-7MKMyokZIF1HTCVqqL0tTWgaC1ZGZBLLltd11ZUhQTswo_8kvgiTv3cFShj7ATF0tAY8HStyJmzLO8mKPVOPDXSwjdNsPriZclI6JWGi9iOu8pEiN9pZM6-itxan1JMcDUNg2U-P1BmKppHRbDKsOTivymfRyeUk51dBIlS54p5xNK1HFLc1YtWC1Rc_ngYVqOgqlhIrCHArAEBe3jrfUaH2ym-8fkVdwVqtxmte3XXK9g8FchsygRNnOKtRcr0TyzTUV-7bPi8_t02Zi-EHLFaSawVXWV_Qk1GeLYJR22Rp74beo-b5-lCNVp10btO0xdGySUWmCJ4v4_QZw.c8unwWycwtfdJMM_0b0fuA", "key": { diff --git a/cas/stepcas/x5c_issuer.go b/cas/stepcas/x5c_issuer.go index da4aa27e..636d22f9 100644 --- a/cas/stepcas/x5c_issuer.go +++ b/cas/stepcas/x5c_issuer.go @@ -143,7 +143,11 @@ func newX5CSigner(certFile, keyFile, password string) (jose.Signer, error) { if err != nil { return nil, err } - certs, err := jose.ValidateX5C(certFile, signer) + certs, err := pemutil.ReadCertificateBundle(certFile) + if err != nil { + return nil, errors.Wrap(err, "error reading x5c certificate chain") + } + certStrs, err := jose.ValidateX5C(certs, signer) if err != nil { return nil, errors.Wrap(err, "error validating x5c certificate chain and key") } @@ -151,7 +155,7 @@ func newX5CSigner(certFile, keyFile, password string) (jose.Signer, error) { so := new(jose.SignerOptions) so.WithType("JWT") so.WithHeader("kid", kid) - so.WithHeader("x5c", certs) + so.WithHeader("x5c", certStrs) return newJoseSigner(signer, so) } diff --git a/go.mod b/go.mod index 8a2fe4a1..15ea5fa0 100644 --- a/go.mod +++ b/go.mod @@ -23,18 +23,22 @@ require ( github.com/smallstep/nosql v0.3.6 github.com/urfave/cli v1.22.4 go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 - go.step.sm/cli-utils v0.2.0 - go.step.sm/crypto v0.8.3 + go.step.sm/cli-utils v0.4.1 + go.step.sm/crypto v0.9.0 + go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 // indirect golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/net v0.0.0-20210119194325-5f4716e94777 google.golang.org/api v0.33.0 google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 - google.golang.org/grpc v1.32.0 - google.golang.org/protobuf v1.25.0 + google.golang.org/grpc v1.38.0 + google.golang.org/protobuf v1.26.0 gopkg.in/square/go-jose.v2 v2.5.1 ) // replace github.com/smallstep/nosql => ../nosql -// replace go.step.sm/crypto => ../crypto + +//replace go.step.sm/crypto => ../crypto + +//replace go.step.sm/cli-utils => ../cli-utils replace go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 => github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 diff --git a/go.sum b/go.sum index 80b154e1..34aea910 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,7 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -56,41 +57,60 @@ github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TN github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8= github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.29 h1:NXNqBS9hjOCpDL8SyCyl38gZX3LLLunKOJc5E7vJ8P0= github.com/aws/aws-sdk-go v1.30.29/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go-v2 v0.18.0 h1:qZ+woO4SamnH/eEbjM2IDLhRNwIwND/RQyVlBLp3Jqg= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -98,12 +118,17 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -111,13 +136,16 @@ github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMu github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -129,6 +157,7 @@ github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20201003150343-5d1bab4fc658/go.mod h github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd h1:KoJOtZf+6wpQaDTuOWGuo61GxcPBIfhwRxRTaTWGCTc= github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd/go.mod h1:YylP9MpCYGVZQrly/j/diqcdUetCRRePeBB0c2VGXsA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= @@ -136,22 +165,32 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db h1:gb2Z18BhTPJPpLQWj4T+rfKHYCHxRHCtRxhKKjRidVw= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= @@ -180,9 +219,11 @@ github.com/go-stack/stack v1.6.0 h1:MmJCxYVKTJ0SplGKqFVX3SBnmaUhODHZrrFF6jMbpZk= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -214,6 +255,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= @@ -230,6 +273,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -253,43 +299,70 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0 h1:0U6+BtN6LhaYuTnIJq4Wyq5cpn6O2kWrxAtcqBmYY6w= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= @@ -298,33 +371,35 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -<<<<<<< HEAD -======= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= ->>>>>>> 7ad90d1 (Refactor initialization of SCEP authority) github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -332,11 +407,14 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743 h1:143Bb8f8DuGWck/xpNUOckBVYfFbBTnLevfRZ1aVVqo= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1 h1:vi1F1IQ8N7hNWytK9DpJsUfQhGuNSc19z330K6vl4zk= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13 h1:KNt/RhmQTOLr7Aj8PsJ7mTronaFyx80mRTT9qF261dA= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -350,33 +428,28 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -<<<<<<< HEAD -<<<<<<< HEAD -======= -github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23 h1:QACkVsQ7Qx4PuPDFL2OFD5u4OnYT0TkReWk9IVqaGHA= -github.com/micromdm/scep v1.0.1-0.20210219005251-6027e7654b23/go.mod h1:a82oNZyGV8jj9Do7dh8EkA90+esBls0CZHR6B85Qda8= ->>>>>>> 7ad90d1 (Refactor initialization of SCEP authority) -github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ= -github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/micromdm/scep v1.0.0 h1:ai//kcZnxZPq1YE/MatiE2bIRD94KOAwZRpN1fhVQXY= -github.com/micromdm/scep v1.0.0/go.mod h1:CID2SixSr5FvoauZdAFUSpQkn5MAuSy9oyURMGOJbag= -======= +github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micromdm/scep/v2 v2.0.0 h1:cRzcY0S5QX+0+J+7YC4P2uZSnfMup8S8zJu/bLFgOkA= github.com/micromdm/scep/v2 v2.0.0/go.mod h1:ouaDs5tcjOjdHD/h8BGaQsWE87MUnQ/wMTMgfMMIpPc= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= ->>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -384,70 +457,101 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/newrelic/go-agent v2.15.0+incompatible h1:IB0Fy+dClpBq9aEoIrLyQXzU34JyI1xVTanPLB/+jvU= github.com/newrelic/go-agent v2.15.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ= +github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568 h1:+MPqEswjYiS0S1FCTg8MIhMBMzxiVQ94rooFwvPPiWk= github.com/omorsi/pkcs7 v0.0.0-20210217142924-a7b80a2a8568/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4 h1:OYkFijGHoZAYbOIb1LWXrwKQbMMRUv1oQ89blD2Mh2Q= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -457,10 +561,13 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -472,9 +579,13 @@ github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1 github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/smallstep/nosql v0.3.6 h1:cq6a3NwjFJxkVlWU1T4qGskcfEXr0fO1WqQrraDO1Po= github.com/smallstep/nosql v0.3.6/go.mod h1:h1zC/Z54uNHc8euquLED4qJNCrMHd3nytA141ZZh4qQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -495,7 +606,9 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= @@ -507,6 +620,7 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -515,6 +629,7 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -526,14 +641,10 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -<<<<<<< HEAD -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -======= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738 h1:VcrIfasaLFkyjk6KNlXQSzO+B0fZcnECiDrKJsfxka0= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= ->>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -543,23 +654,32 @@ go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.step.sm/cli-utils v0.2.0 h1:hpVu9+6dpv/7/Bd8nGJFc3V+gQ+TciSJRTu9TavDUQ4= go.step.sm/cli-utils v0.2.0/go.mod h1:+t4qCp5NO+080DdGkJxEh3xL5S4TcYC2JTPLMM72b6Y= +go.step.sm/cli-utils v0.4.0 h1:dni6gR/6/LOqfbzm/yUdgz5O12tkxX17SxA9+pRMidI= +go.step.sm/cli-utils v0.4.0/go.mod h1:1zFgatDqEJ1Y4MNStdWa0b1NPc1fvSHbDJC+wZ6iQlE= +go.step.sm/cli-utils v0.4.1 h1:QztRUhGYjOPM1I2Nmi7V6XejQyVtcESmo+sbegxvX7Q= +go.step.sm/cli-utils v0.4.1/go.mod h1:hWYVOSlw8W9Pd+BwIbs/aftVVMRms3EG7Q2qLRwc0WA= go.step.sm/crypto v0.6.1/go.mod h1:AKS4yMZVZD4EGjpSkY4eibuMenrvKCscb+BpWMet8c0= -<<<<<<< HEAD go.step.sm/crypto v0.8.3 h1:TO/OPlaUrYXhs8srGEFNyL6OWVQvRmEPCUONNnQUuEM= go.step.sm/crypto v0.8.3/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= -======= -go.step.sm/crypto v0.8.0 h1:S4qBPyy3hR7KWLybSkHB0H14pwFfYkom4RZ96JzmXig= -go.step.sm/crypto v0.8.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +go.step.sm/crypto v0.9.0 h1:q2AllTSnVj4NRtyEPkGW2ohArLmbGbe6ZAL/VIOKDzA= +go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0= +go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7 h1:hAfzUm80XWGtFnxyVgeT/gc/3XnlVNnHD5HrLbk4Fc0= +go.step.sm/linkedca v0.0.0-20210610014030-59b16916c7e7/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= +go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25 h1:ncJqviWswJT19IdnfOYQGKG1zL7IDy4lAJz1PuM3fgw= +go.step.sm/linkedca v0.0.0-20210611183751-27424aae8d25/go.mod h1:5uTRjozEGSPAZal9xJqlaD38cvJcLe3o1VAFVjqcORo= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= ->>>>>>> c3d9cef (Update to v2.0.0 of github.com/micromdm/scep) golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -569,8 +689,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -642,9 +762,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -854,6 +973,8 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -865,20 +986,30 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -900,5 +1031,7 @@ rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/linkedca/admin.pb.go b/linkedca/admin.pb.go deleted file mode 100644 index 02e287c1..00000000 --- a/linkedca/admin.pb.go +++ /dev/null @@ -1,240 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.26.0 -// protoc v3.15.8 -// source: linkedca/admin.proto - -package linkedca - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Admin_Type int32 - -const ( - Admin_UNKNOWN Admin_Type = 0 - Admin_ADMIN Admin_Type = 1 - Admin_SUPER_ADMIN Admin_Type = 2 -) - -// Enum value maps for Admin_Type. -var ( - Admin_Type_name = map[int32]string{ - 0: "UNKNOWN", - 1: "ADMIN", - 2: "SUPER_ADMIN", - } - Admin_Type_value = map[string]int32{ - "UNKNOWN": 0, - "ADMIN": 1, - "SUPER_ADMIN": 2, - } -) - -func (x Admin_Type) Enum() *Admin_Type { - p := new(Admin_Type) - *p = x - return p -} - -func (x Admin_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Admin_Type) Descriptor() protoreflect.EnumDescriptor { - return file_linkedca_admin_proto_enumTypes[0].Descriptor() -} - -func (Admin_Type) Type() protoreflect.EnumType { - return &file_linkedca_admin_proto_enumTypes[0] -} - -func (x Admin_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Admin_Type.Descriptor instead. -func (Admin_Type) EnumDescriptor() ([]byte, []int) { - return file_linkedca_admin_proto_rawDescGZIP(), []int{0, 0} -} - -type Admin struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Subject string `protobuf:"bytes,3,opt,name=subject,proto3" json:"subject,omitempty"` - ProvisionerId string `protobuf:"bytes,4,opt,name=provisioner_id,json=provisionerId,proto3" json:"provisioner_id,omitempty"` - Type Admin_Type `protobuf:"varint,5,opt,name=type,proto3,enum=linkedca.Admin_Type" json:"type,omitempty"` -} - -func (x *Admin) Reset() { - *x = Admin{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_admin_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Admin) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Admin) ProtoMessage() {} - -func (x *Admin) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_admin_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Admin.ProtoReflect.Descriptor instead. -func (*Admin) Descriptor() ([]byte, []int) { - return file_linkedca_admin_proto_rawDescGZIP(), []int{0} -} - -func (x *Admin) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Admin) GetAuthorityId() string { - if x != nil { - return x.AuthorityId - } - return "" -} - -func (x *Admin) GetSubject() string { - if x != nil { - return x.Subject - } - return "" -} - -func (x *Admin) GetProvisionerId() string { - if x != nil { - return x.ProvisionerId - } - return "" -} - -func (x *Admin) GetType() Admin_Type { - if x != nil { - return x.Type - } - return Admin_UNKNOWN -} - -var File_linkedca_admin_proto protoreflect.FileDescriptor - -var file_linkedca_admin_proto_rawDesc = []byte{ - 0x0a, 0x14, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, - 0x22, 0xd6, 0x01, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x2e, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, - 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x55, 0x50, 0x45, - 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, - 0x70, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_linkedca_admin_proto_rawDescOnce sync.Once - file_linkedca_admin_proto_rawDescData = file_linkedca_admin_proto_rawDesc -) - -func file_linkedca_admin_proto_rawDescGZIP() []byte { - file_linkedca_admin_proto_rawDescOnce.Do(func() { - file_linkedca_admin_proto_rawDescData = protoimpl.X.CompressGZIP(file_linkedca_admin_proto_rawDescData) - }) - return file_linkedca_admin_proto_rawDescData -} - -var file_linkedca_admin_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_linkedca_admin_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_linkedca_admin_proto_goTypes = []interface{}{ - (Admin_Type)(0), // 0: linkedca.Admin.Type - (*Admin)(nil), // 1: linkedca.Admin -} -var file_linkedca_admin_proto_depIdxs = []int32{ - 0, // 0: linkedca.Admin.type:type_name -> linkedca.Admin.Type - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_linkedca_admin_proto_init() } -func file_linkedca_admin_proto_init() { - if File_linkedca_admin_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_linkedca_admin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Admin); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_linkedca_admin_proto_rawDesc, - NumEnums: 1, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_linkedca_admin_proto_goTypes, - DependencyIndexes: file_linkedca_admin_proto_depIdxs, - EnumInfos: file_linkedca_admin_proto_enumTypes, - MessageInfos: file_linkedca_admin_proto_msgTypes, - }.Build() - File_linkedca_admin_proto = out.File - file_linkedca_admin_proto_rawDesc = nil - file_linkedca_admin_proto_goTypes = nil - file_linkedca_admin_proto_depIdxs = nil -} diff --git a/linkedca/admin.proto b/linkedca/admin.proto deleted file mode 100644 index 29c76139..00000000 --- a/linkedca/admin.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -package linkedca; - -option go_package = "github.com/smallstep/certificates/linkedca"; - -message Admin { - enum Type { - UNKNOWN = 0; - ADMIN = 1; - SUPER_ADMIN = 2; - } - string id = 1; - string authority_id = 2; - string subject = 3; - string provisioner_id = 4; - Type type = 5; -} diff --git a/linkedca/doc.go b/linkedca/doc.go deleted file mode 100644 index ceddecb0..00000000 --- a/linkedca/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -package linkedca - -//go:generate protoc --proto_path=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative linkedca/provisioners.proto linkedca/admin.proto diff --git a/linkedca/provisioners.go b/linkedca/provisioners.go deleted file mode 100644 index ea46f342..00000000 --- a/linkedca/provisioners.go +++ /dev/null @@ -1,38 +0,0 @@ -package linkedca - -import ( - "encoding/json" - "fmt" -) - -// UnmarshalProvisionerDetails unmarshals details type to the specific provisioner details. -func UnmarshalProvisionerDetails(typ Provisioner_Type, data []byte) (*ProvisionerDetails, error) { - var v isProvisionerDetails_Data - switch typ { - case Provisioner_JWK: - v = new(ProvisionerDetails_JWK) - case Provisioner_OIDC: - v = new(ProvisionerDetails_OIDC) - case Provisioner_GCP: - v = new(ProvisionerDetails_GCP) - case Provisioner_AWS: - v = new(ProvisionerDetails_AWS) - case Provisioner_AZURE: - v = new(ProvisionerDetails_Azure) - case Provisioner_ACME: - v = new(ProvisionerDetails_ACME) - case Provisioner_X5C: - v = new(ProvisionerDetails_X5C) - case Provisioner_K8SSA: - v = new(ProvisionerDetails_K8SSA) - case Provisioner_SSHPOP: - v = new(ProvisionerDetails_SSHPOP) - default: - return nil, fmt.Errorf("unsupported provisioner type %s", typ) - } - - if err := json.Unmarshal(data, v); err != nil { - return nil, err - } - return &ProvisionerDetails{Data: v}, nil -} diff --git a/linkedca/provisioners.pb.go b/linkedca/provisioners.pb.go deleted file mode 100644 index 4b8ffab0..00000000 --- a/linkedca/provisioners.pb.go +++ /dev/null @@ -1,1833 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.26.0 -// protoc v3.15.8 -// source: linkedca/provisioners.proto - -package linkedca - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Provisioner_Type int32 - -const ( - Provisioner_NOOP Provisioner_Type = 0 - Provisioner_JWK Provisioner_Type = 1 - Provisioner_OIDC Provisioner_Type = 2 - Provisioner_GCP Provisioner_Type = 3 - Provisioner_AWS Provisioner_Type = 4 - Provisioner_AZURE Provisioner_Type = 5 - Provisioner_ACME Provisioner_Type = 6 - Provisioner_X5C Provisioner_Type = 7 - Provisioner_K8SSA Provisioner_Type = 8 - Provisioner_SSHPOP Provisioner_Type = 9 - Provisioner_SCEP Provisioner_Type = 10 -) - -// Enum value maps for Provisioner_Type. -var ( - Provisioner_Type_name = map[int32]string{ - 0: "NOOP", - 1: "JWK", - 2: "OIDC", - 3: "GCP", - 4: "AWS", - 5: "AZURE", - 6: "ACME", - 7: "X5C", - 8: "K8SSA", - 9: "SSHPOP", - 10: "SCEP", - } - Provisioner_Type_value = map[string]int32{ - "NOOP": 0, - "JWK": 1, - "OIDC": 2, - "GCP": 3, - "AWS": 4, - "AZURE": 5, - "ACME": 6, - "X5C": 7, - "K8SSA": 8, - "SSHPOP": 9, - "SCEP": 10, - } -) - -func (x Provisioner_Type) Enum() *Provisioner_Type { - p := new(Provisioner_Type) - *p = x - return p -} - -func (x Provisioner_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Provisioner_Type) Descriptor() protoreflect.EnumDescriptor { - return file_linkedca_provisioners_proto_enumTypes[0].Descriptor() -} - -func (Provisioner_Type) Type() protoreflect.EnumType { - return &file_linkedca_provisioners_proto_enumTypes[0] -} - -func (x Provisioner_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Provisioner_Type.Descriptor instead. -func (Provisioner_Type) EnumDescriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{0, 0} -} - -type Provisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - AuthorityId string `protobuf:"bytes,2,opt,name=authority_id,json=authorityId,proto3" json:"authority_id,omitempty"` - Type Provisioner_Type `protobuf:"varint,3,opt,name=type,proto3,enum=linkedca.Provisioner_Type" json:"type,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - Details *ProvisionerDetails `protobuf:"bytes,5,opt,name=details,proto3" json:"details,omitempty"` - Claims *Claims `protobuf:"bytes,6,opt,name=claims,proto3" json:"claims,omitempty"` - X509Template []byte `protobuf:"bytes,7,opt,name=x509_template,json=x509Template,proto3" json:"x509_template,omitempty"` - X509TemplateData []byte `protobuf:"bytes,8,opt,name=x509_template_data,json=x509TemplateData,proto3" json:"x509_template_data,omitempty"` - SshTemplate []byte `protobuf:"bytes,9,opt,name=ssh_template,json=sshTemplate,proto3" json:"ssh_template,omitempty"` - SshTemplateData []byte `protobuf:"bytes,10,opt,name=ssh_template_data,json=sshTemplateData,proto3" json:"ssh_template_data,omitempty"` -} - -func (x *Provisioner) Reset() { - *x = Provisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Provisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Provisioner) ProtoMessage() {} - -func (x *Provisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Provisioner.ProtoReflect.Descriptor instead. -func (*Provisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{0} -} - -func (x *Provisioner) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Provisioner) GetAuthorityId() string { - if x != nil { - return x.AuthorityId - } - return "" -} - -func (x *Provisioner) GetType() Provisioner_Type { - if x != nil { - return x.Type - } - return Provisioner_NOOP -} - -func (x *Provisioner) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Provisioner) GetDetails() *ProvisionerDetails { - if x != nil { - return x.Details - } - return nil -} - -func (x *Provisioner) GetClaims() *Claims { - if x != nil { - return x.Claims - } - return nil -} - -func (x *Provisioner) GetX509Template() []byte { - if x != nil { - return x.X509Template - } - return nil -} - -func (x *Provisioner) GetX509TemplateData() []byte { - if x != nil { - return x.X509TemplateData - } - return nil -} - -func (x *Provisioner) GetSshTemplate() []byte { - if x != nil { - return x.SshTemplate - } - return nil -} - -func (x *Provisioner) GetSshTemplateData() []byte { - if x != nil { - return x.SshTemplateData - } - return nil -} - -type ProvisionerDetails struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Data: - // *ProvisionerDetails_JWK - // *ProvisionerDetails_OIDC - // *ProvisionerDetails_GCP - // *ProvisionerDetails_AWS - // *ProvisionerDetails_Azure - // *ProvisionerDetails_ACME - // *ProvisionerDetails_X5C - // *ProvisionerDetails_K8SSA - // *ProvisionerDetails_SSHPOP - // *ProvisionerDetails_SCEP - Data isProvisionerDetails_Data `protobuf_oneof:"data"` -} - -func (x *ProvisionerDetails) Reset() { - *x = ProvisionerDetails{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProvisionerDetails) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProvisionerDetails) ProtoMessage() {} - -func (x *ProvisionerDetails) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProvisionerDetails.ProtoReflect.Descriptor instead. -func (*ProvisionerDetails) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{1} -} - -func (m *ProvisionerDetails) GetData() isProvisionerDetails_Data { - if m != nil { - return m.Data - } - return nil -} - -func (x *ProvisionerDetails) GetJWK() *JWKProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_JWK); ok { - return x.JWK - } - return nil -} - -func (x *ProvisionerDetails) GetOIDC() *OIDCProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_OIDC); ok { - return x.OIDC - } - return nil -} - -func (x *ProvisionerDetails) GetGCP() *GCPProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_GCP); ok { - return x.GCP - } - return nil -} - -func (x *ProvisionerDetails) GetAWS() *AWSProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_AWS); ok { - return x.AWS - } - return nil -} - -func (x *ProvisionerDetails) GetAzure() *AzureProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_Azure); ok { - return x.Azure - } - return nil -} - -func (x *ProvisionerDetails) GetACME() *ACMEProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_ACME); ok { - return x.ACME - } - return nil -} - -func (x *ProvisionerDetails) GetX5C() *X5CProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_X5C); ok { - return x.X5C - } - return nil -} - -func (x *ProvisionerDetails) GetK8SSA() *K8SSAProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_K8SSA); ok { - return x.K8SSA - } - return nil -} - -func (x *ProvisionerDetails) GetSSHPOP() *SSHPOPProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_SSHPOP); ok { - return x.SSHPOP - } - return nil -} - -func (x *ProvisionerDetails) GetSCEP() *SCEPProvisioner { - if x, ok := x.GetData().(*ProvisionerDetails_SCEP); ok { - return x.SCEP - } - return nil -} - -type isProvisionerDetails_Data interface { - isProvisionerDetails_Data() -} - -type ProvisionerDetails_JWK struct { - JWK *JWKProvisioner `protobuf:"bytes,20,opt,name=JWK,proto3,oneof"` -} - -type ProvisionerDetails_OIDC struct { - OIDC *OIDCProvisioner `protobuf:"bytes,21,opt,name=OIDC,proto3,oneof"` -} - -type ProvisionerDetails_GCP struct { - GCP *GCPProvisioner `protobuf:"bytes,22,opt,name=GCP,proto3,oneof"` -} - -type ProvisionerDetails_AWS struct { - AWS *AWSProvisioner `protobuf:"bytes,23,opt,name=AWS,proto3,oneof"` -} - -type ProvisionerDetails_Azure struct { - Azure *AzureProvisioner `protobuf:"bytes,24,opt,name=Azure,proto3,oneof"` -} - -type ProvisionerDetails_ACME struct { - ACME *ACMEProvisioner `protobuf:"bytes,25,opt,name=ACME,proto3,oneof"` -} - -type ProvisionerDetails_X5C struct { - X5C *X5CProvisioner `protobuf:"bytes,26,opt,name=X5C,proto3,oneof"` -} - -type ProvisionerDetails_K8SSA struct { - K8SSA *K8SSAProvisioner `protobuf:"bytes,27,opt,name=K8sSA,proto3,oneof"` -} - -type ProvisionerDetails_SSHPOP struct { - SSHPOP *SSHPOPProvisioner `protobuf:"bytes,28,opt,name=SSHPOP,proto3,oneof"` -} - -type ProvisionerDetails_SCEP struct { - SCEP *SCEPProvisioner `protobuf:"bytes,29,opt,name=SCEP,proto3,oneof"` -} - -func (*ProvisionerDetails_JWK) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_OIDC) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_GCP) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_AWS) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_Azure) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_ACME) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_X5C) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_K8SSA) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_SSHPOP) isProvisionerDetails_Data() {} - -func (*ProvisionerDetails_SCEP) isProvisionerDetails_Data() {} - -type ProvisionerList struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Provisioners []*Provisioner `protobuf:"bytes,1,rep,name=provisioners,proto3" json:"provisioners,omitempty"` -} - -func (x *ProvisionerList) Reset() { - *x = ProvisionerList{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ProvisionerList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProvisionerList) ProtoMessage() {} - -func (x *ProvisionerList) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProvisionerList.ProtoReflect.Descriptor instead. -func (*ProvisionerList) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{2} -} - -func (x *ProvisionerList) GetProvisioners() []*Provisioner { - if x != nil { - return x.Provisioners - } - return nil -} - -type Claims struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - X509 *X509Claims `protobuf:"bytes,1,opt,name=x509,proto3" json:"x509,omitempty"` - Ssh *SSHClaims `protobuf:"bytes,2,opt,name=ssh,proto3" json:"ssh,omitempty"` - DisableRenewal bool `protobuf:"varint,3,opt,name=disable_renewal,json=disableRenewal,proto3" json:"disable_renewal,omitempty"` -} - -func (x *Claims) Reset() { - *x = Claims{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Claims) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Claims) ProtoMessage() {} - -func (x *Claims) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Claims.ProtoReflect.Descriptor instead. -func (*Claims) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{3} -} - -func (x *Claims) GetX509() *X509Claims { - if x != nil { - return x.X509 - } - return nil -} - -func (x *Claims) GetSsh() *SSHClaims { - if x != nil { - return x.Ssh - } - return nil -} - -func (x *Claims) GetDisableRenewal() bool { - if x != nil { - return x.DisableRenewal - } - return false -} - -type X509Claims struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Durations *Durations `protobuf:"bytes,2,opt,name=durations,proto3" json:"durations,omitempty"` -} - -func (x *X509Claims) Reset() { - *x = X509Claims{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *X509Claims) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*X509Claims) ProtoMessage() {} - -func (x *X509Claims) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use X509Claims.ProtoReflect.Descriptor instead. -func (*X509Claims) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{4} -} - -func (x *X509Claims) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *X509Claims) GetDurations() *Durations { - if x != nil { - return x.Durations - } - return nil -} - -type SSHClaims struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - UserDurations *Durations `protobuf:"bytes,2,opt,name=user_durations,json=userDurations,proto3" json:"user_durations,omitempty"` - HostDurations *Durations `protobuf:"bytes,3,opt,name=host_durations,json=hostDurations,proto3" json:"host_durations,omitempty"` -} - -func (x *SSHClaims) Reset() { - *x = SSHClaims{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SSHClaims) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SSHClaims) ProtoMessage() {} - -func (x *SSHClaims) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SSHClaims.ProtoReflect.Descriptor instead. -func (*SSHClaims) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{5} -} - -func (x *SSHClaims) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *SSHClaims) GetUserDurations() *Durations { - if x != nil { - return x.UserDurations - } - return nil -} - -func (x *SSHClaims) GetHostDurations() *Durations { - if x != nil { - return x.HostDurations - } - return nil -} - -type Durations struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Default string `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty"` - Min string `protobuf:"bytes,2,opt,name=min,proto3" json:"min,omitempty"` - Max string `protobuf:"bytes,3,opt,name=max,proto3" json:"max,omitempty"` -} - -func (x *Durations) Reset() { - *x = Durations{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Durations) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Durations) ProtoMessage() {} - -func (x *Durations) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Durations.ProtoReflect.Descriptor instead. -func (*Durations) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{6} -} - -func (x *Durations) GetDefault() string { - if x != nil { - return x.Default - } - return "" -} - -func (x *Durations) GetMin() string { - if x != nil { - return x.Min - } - return "" -} - -func (x *Durations) GetMax() string { - if x != nil { - return x.Max - } - return "" -} - -type JWKProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - EncryptedPrivateKey []byte `protobuf:"bytes,2,opt,name=encrypted_private_key,json=encryptedPrivateKey,proto3" json:"encrypted_private_key,omitempty"` -} - -func (x *JWKProvisioner) Reset() { - *x = JWKProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *JWKProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*JWKProvisioner) ProtoMessage() {} - -func (x *JWKProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use JWKProvisioner.ProtoReflect.Descriptor instead. -func (*JWKProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{7} -} - -func (x *JWKProvisioner) GetPublicKey() []byte { - if x != nil { - return x.PublicKey - } - return nil -} - -func (x *JWKProvisioner) GetEncryptedPrivateKey() []byte { - if x != nil { - return x.EncryptedPrivateKey - } - return nil -} - -type OIDCProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` - ClientSecret string `protobuf:"bytes,2,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"` - ConfigurationEndpoint string `protobuf:"bytes,3,opt,name=configuration_endpoint,json=configurationEndpoint,proto3" json:"configuration_endpoint,omitempty"` - Admins []string `protobuf:"bytes,4,rep,name=admins,proto3" json:"admins,omitempty"` - Domains []string `protobuf:"bytes,5,rep,name=domains,proto3" json:"domains,omitempty"` - Groups []string `protobuf:"bytes,6,rep,name=groups,proto3" json:"groups,omitempty"` - ListenAddress string `protobuf:"bytes,7,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` - TenantId string `protobuf:"bytes,8,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` -} - -func (x *OIDCProvisioner) Reset() { - *x = OIDCProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OIDCProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OIDCProvisioner) ProtoMessage() {} - -func (x *OIDCProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OIDCProvisioner.ProtoReflect.Descriptor instead. -func (*OIDCProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{8} -} - -func (x *OIDCProvisioner) GetClientId() string { - if x != nil { - return x.ClientId - } - return "" -} - -func (x *OIDCProvisioner) GetClientSecret() string { - if x != nil { - return x.ClientSecret - } - return "" -} - -func (x *OIDCProvisioner) GetConfigurationEndpoint() string { - if x != nil { - return x.ConfigurationEndpoint - } - return "" -} - -func (x *OIDCProvisioner) GetAdmins() []string { - if x != nil { - return x.Admins - } - return nil -} - -func (x *OIDCProvisioner) GetDomains() []string { - if x != nil { - return x.Domains - } - return nil -} - -func (x *OIDCProvisioner) GetGroups() []string { - if x != nil { - return x.Groups - } - return nil -} - -func (x *OIDCProvisioner) GetListenAddress() string { - if x != nil { - return x.ListenAddress - } - return "" -} - -func (x *OIDCProvisioner) GetTenantId() string { - if x != nil { - return x.TenantId - } - return "" -} - -type GCPProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ServiceAccounts []string `protobuf:"bytes,1,rep,name=service_accounts,json=serviceAccounts,proto3" json:"service_accounts,omitempty"` - ProjectIds []string `protobuf:"bytes,2,rep,name=project_ids,json=projectIds,proto3" json:"project_ids,omitempty"` - DisableCustomSans bool `protobuf:"varint,3,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` - DisableTrustOnFirstUse bool `protobuf:"varint,4,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` - InstanceAge string `protobuf:"bytes,5,opt,name=instance_age,json=instanceAge,proto3" json:"instance_age,omitempty"` -} - -func (x *GCPProvisioner) Reset() { - *x = GCPProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCPProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCPProvisioner) ProtoMessage() {} - -func (x *GCPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCPProvisioner.ProtoReflect.Descriptor instead. -func (*GCPProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{9} -} - -func (x *GCPProvisioner) GetServiceAccounts() []string { - if x != nil { - return x.ServiceAccounts - } - return nil -} - -func (x *GCPProvisioner) GetProjectIds() []string { - if x != nil { - return x.ProjectIds - } - return nil -} - -func (x *GCPProvisioner) GetDisableCustomSans() bool { - if x != nil { - return x.DisableCustomSans - } - return false -} - -func (x *GCPProvisioner) GetDisableTrustOnFirstUse() bool { - if x != nil { - return x.DisableTrustOnFirstUse - } - return false -} - -func (x *GCPProvisioner) GetInstanceAge() string { - if x != nil { - return x.InstanceAge - } - return "" -} - -type AWSProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Accounts []string `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` - DisableCustomSans bool `protobuf:"varint,2,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` - DisableTrustOnFirstUse bool `protobuf:"varint,3,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` - InstanceAge string `protobuf:"bytes,4,opt,name=instance_age,json=instanceAge,proto3" json:"instance_age,omitempty"` -} - -func (x *AWSProvisioner) Reset() { - *x = AWSProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AWSProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AWSProvisioner) ProtoMessage() {} - -func (x *AWSProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AWSProvisioner.ProtoReflect.Descriptor instead. -func (*AWSProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{10} -} - -func (x *AWSProvisioner) GetAccounts() []string { - if x != nil { - return x.Accounts - } - return nil -} - -func (x *AWSProvisioner) GetDisableCustomSans() bool { - if x != nil { - return x.DisableCustomSans - } - return false -} - -func (x *AWSProvisioner) GetDisableTrustOnFirstUse() bool { - if x != nil { - return x.DisableTrustOnFirstUse - } - return false -} - -func (x *AWSProvisioner) GetInstanceAge() string { - if x != nil { - return x.InstanceAge - } - return "" -} - -type AzureProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` - ResourceGroups []string `protobuf:"bytes,2,rep,name=resource_groups,json=resourceGroups,proto3" json:"resource_groups,omitempty"` - Audience string `protobuf:"bytes,3,opt,name=audience,proto3" json:"audience,omitempty"` - DisableCustomSans bool `protobuf:"varint,4,opt,name=disable_custom_sans,json=disableCustomSans,proto3" json:"disable_custom_sans,omitempty"` - DisableTrustOnFirstUse bool `protobuf:"varint,5,opt,name=disable_trust_on_first_use,json=disableTrustOnFirstUse,proto3" json:"disable_trust_on_first_use,omitempty"` -} - -func (x *AzureProvisioner) Reset() { - *x = AzureProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AzureProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AzureProvisioner) ProtoMessage() {} - -func (x *AzureProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AzureProvisioner.ProtoReflect.Descriptor instead. -func (*AzureProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{11} -} - -func (x *AzureProvisioner) GetTenantId() string { - if x != nil { - return x.TenantId - } - return "" -} - -func (x *AzureProvisioner) GetResourceGroups() []string { - if x != nil { - return x.ResourceGroups - } - return nil -} - -func (x *AzureProvisioner) GetAudience() string { - if x != nil { - return x.Audience - } - return "" -} - -func (x *AzureProvisioner) GetDisableCustomSans() bool { - if x != nil { - return x.DisableCustomSans - } - return false -} - -func (x *AzureProvisioner) GetDisableTrustOnFirstUse() bool { - if x != nil { - return x.DisableTrustOnFirstUse - } - return false -} - -type ACMEProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ForceCn bool `protobuf:"varint,1,opt,name=force_cn,json=forceCn,proto3" json:"force_cn,omitempty"` -} - -func (x *ACMEProvisioner) Reset() { - *x = ACMEProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ACMEProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ACMEProvisioner) ProtoMessage() {} - -func (x *ACMEProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ACMEProvisioner.ProtoReflect.Descriptor instead. -func (*ACMEProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{12} -} - -func (x *ACMEProvisioner) GetForceCn() bool { - if x != nil { - return x.ForceCn - } - return false -} - -type X5CProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Roots [][]byte `protobuf:"bytes,1,rep,name=roots,proto3" json:"roots,omitempty"` -} - -func (x *X5CProvisioner) Reset() { - *x = X5CProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *X5CProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*X5CProvisioner) ProtoMessage() {} - -func (x *X5CProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use X5CProvisioner.ProtoReflect.Descriptor instead. -func (*X5CProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{13} -} - -func (x *X5CProvisioner) GetRoots() [][]byte { - if x != nil { - return x.Roots - } - return nil -} - -type K8SSAProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PublicKeys [][]byte `protobuf:"bytes,1,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty"` -} - -func (x *K8SSAProvisioner) Reset() { - *x = K8SSAProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *K8SSAProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*K8SSAProvisioner) ProtoMessage() {} - -func (x *K8SSAProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use K8SSAProvisioner.ProtoReflect.Descriptor instead. -func (*K8SSAProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{14} -} - -func (x *K8SSAProvisioner) GetPublicKeys() [][]byte { - if x != nil { - return x.PublicKeys - } - return nil -} - -type SSHPOPProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SSHPOPProvisioner) Reset() { - *x = SSHPOPProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SSHPOPProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SSHPOPProvisioner) ProtoMessage() {} - -func (x *SSHPOPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SSHPOPProvisioner.ProtoReflect.Descriptor instead. -func (*SSHPOPProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{15} -} - -type SCEPProvisioner struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ForceCn bool `protobuf:"varint,1,opt,name=force_cn,json=forceCn,proto3" json:"force_cn,omitempty"` - Challenge string `protobuf:"bytes,2,opt,name=challenge,proto3" json:"challenge,omitempty"` - Capabilities []string `protobuf:"bytes,3,rep,name=capabilities,proto3" json:"capabilities,omitempty"` - MinimumPublicKeyLength int32 `protobuf:"varint,4,opt,name=minimum_public_key_length,json=minimumPublicKeyLength,proto3" json:"minimum_public_key_length,omitempty"` -} - -func (x *SCEPProvisioner) Reset() { - *x = SCEPProvisioner{} - if protoimpl.UnsafeEnabled { - mi := &file_linkedca_provisioners_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SCEPProvisioner) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SCEPProvisioner) ProtoMessage() {} - -func (x *SCEPProvisioner) ProtoReflect() protoreflect.Message { - mi := &file_linkedca_provisioners_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SCEPProvisioner.ProtoReflect.Descriptor instead. -func (*SCEPProvisioner) Descriptor() ([]byte, []int) { - return file_linkedca_provisioners_proto_rawDescGZIP(), []int{16} -} - -func (x *SCEPProvisioner) GetForceCn() bool { - if x != nil { - return x.ForceCn - } - return false -} - -func (x *SCEPProvisioner) GetChallenge() string { - if x != nil { - return x.Challenge - } - return "" -} - -func (x *SCEPProvisioner) GetCapabilities() []string { - if x != nil { - return x.Capabilities - } - return nil -} - -func (x *SCEPProvisioner) GetMinimumPublicKeyLength() int32 { - if x != nil { - return x.MinimumPublicKeyLength - } - return 0 -} - -var File_linkedca_provisioners_proto protoreflect.FileDescriptor - -var file_linkedca_provisioners_proto_rawDesc = []byte{ - 0x0a, 0x1b, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x22, 0xfe, 0x03, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, - 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x07, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x28, 0x0a, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, - 0x61, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x06, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x10, 0x78, 0x35, 0x30, 0x39, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x73, 0x68, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x73, 0x68, 0x5f, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0f, 0x73, 0x73, 0x68, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x22, 0x74, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, - 0x4f, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x10, 0x01, 0x12, 0x08, 0x0a, - 0x04, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x43, 0x50, 0x10, 0x03, - 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x5a, 0x55, - 0x52, 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x10, 0x06, 0x12, 0x07, - 0x0a, 0x03, 0x58, 0x35, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x38, 0x53, 0x53, 0x41, - 0x10, 0x08, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x10, 0x09, 0x12, 0x08, - 0x0a, 0x04, 0x53, 0x43, 0x45, 0x50, 0x10, 0x0a, 0x22, 0x86, 0x04, 0x0a, 0x12, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, - 0x2c, 0x0a, 0x03, 0x4a, 0x57, 0x4b, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4a, 0x57, 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x4a, 0x57, 0x4b, 0x12, 0x2f, 0x0a, - 0x04, 0x4f, 0x49, 0x44, 0x43, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x12, 0x2c, - 0x0a, 0x03, 0x47, 0x43, 0x50, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x47, 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x47, 0x43, 0x50, 0x12, 0x2c, 0x0a, 0x03, - 0x41, 0x57, 0x53, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x57, 0x53, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x41, 0x57, 0x53, 0x12, 0x32, 0x0a, 0x05, 0x41, 0x7a, - 0x75, 0x72, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x41, 0x7a, 0x75, 0x72, 0x65, 0x12, 0x2f, - 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x41, 0x43, 0x4d, 0x45, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, - 0x2c, 0x0a, 0x03, 0x58, 0x35, 0x43, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x58, 0x35, 0x43, 0x12, 0x32, 0x0a, - 0x05, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, - 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x4b, 0x38, 0x73, 0x53, - 0x41, 0x12, 0x35, 0x0a, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x18, 0x1c, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, - 0x50, 0x4f, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x48, 0x00, - 0x52, 0x06, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x12, 0x2f, 0x0a, 0x04, 0x53, 0x43, 0x45, 0x50, - 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, - 0x61, 0x2e, 0x53, 0x43, 0x45, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x48, 0x00, 0x52, 0x04, 0x53, 0x43, 0x45, 0x50, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x22, 0x4c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x69, 0x6e, - 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x22, - 0x82, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x78, 0x35, - 0x30, 0x39, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x64, 0x63, 0x61, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x04, - 0x78, 0x35, 0x30, 0x39, 0x12, 0x25, 0x0a, 0x03, 0x73, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x53, 0x53, 0x48, - 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x52, 0x03, 0x73, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6e, - 0x65, 0x77, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0a, 0x58, 0x35, 0x30, 0x39, 0x43, 0x6c, 0x61, 0x69, - 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x09, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x9d, 0x01, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x65, 0x64, 0x63, 0x61, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x49, 0x0a, 0x09, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x78, 0x22, 0x63, 0x0a, 0x0e, 0x4a, 0x57, - 0x4b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x65, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x65, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x22, - 0x98, 0x02, 0x0a, 0x0f, 0x4f, 0x49, 0x44, 0x43, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, - 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xeb, 0x01, 0x0a, 0x0e, 0x47, - 0x43, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, - 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, - 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, - 0x73, 0x74, 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x0e, 0x41, 0x57, 0x53, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x61, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x55, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, - 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x41, 0x67, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x41, 0x7a, 0x75, 0x72, 0x65, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, - 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, - 0x73, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x61, 0x6e, 0x73, 0x12, 0x3a, 0x0a, - 0x1a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x5f, 0x6f, - 0x6e, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, - 0x6e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x55, 0x73, 0x65, 0x22, 0x2c, 0x0a, 0x0f, 0x41, 0x43, 0x4d, - 0x45, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x22, 0x26, 0x0a, 0x0e, 0x58, 0x35, 0x43, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x6f, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x6f, 0x6f, 0x74, 0x73, 0x22, - 0x33, 0x0a, 0x10, 0x4b, 0x38, 0x73, 0x53, 0x41, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, - 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x53, 0x53, 0x48, 0x50, 0x4f, 0x50, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x22, 0xa9, 0x01, 0x0a, 0x0f, 0x53, 0x43, - 0x45, 0x50, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, - 0x08, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6c, - 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, - 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, - 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x6d, 0x69, - 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x6d, - 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x4c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x73, 0x74, 0x65, 0x70, 0x2f, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, - 0x64, 0x63, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_linkedca_provisioners_proto_rawDescOnce sync.Once - file_linkedca_provisioners_proto_rawDescData = file_linkedca_provisioners_proto_rawDesc -) - -func file_linkedca_provisioners_proto_rawDescGZIP() []byte { - file_linkedca_provisioners_proto_rawDescOnce.Do(func() { - file_linkedca_provisioners_proto_rawDescData = protoimpl.X.CompressGZIP(file_linkedca_provisioners_proto_rawDescData) - }) - return file_linkedca_provisioners_proto_rawDescData -} - -var file_linkedca_provisioners_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_linkedca_provisioners_proto_msgTypes = make([]protoimpl.MessageInfo, 17) -var file_linkedca_provisioners_proto_goTypes = []interface{}{ - (Provisioner_Type)(0), // 0: linkedca.Provisioner.Type - (*Provisioner)(nil), // 1: linkedca.Provisioner - (*ProvisionerDetails)(nil), // 2: linkedca.ProvisionerDetails - (*ProvisionerList)(nil), // 3: linkedca.ProvisionerList - (*Claims)(nil), // 4: linkedca.Claims - (*X509Claims)(nil), // 5: linkedca.X509Claims - (*SSHClaims)(nil), // 6: linkedca.SSHClaims - (*Durations)(nil), // 7: linkedca.Durations - (*JWKProvisioner)(nil), // 8: linkedca.JWKProvisioner - (*OIDCProvisioner)(nil), // 9: linkedca.OIDCProvisioner - (*GCPProvisioner)(nil), // 10: linkedca.GCPProvisioner - (*AWSProvisioner)(nil), // 11: linkedca.AWSProvisioner - (*AzureProvisioner)(nil), // 12: linkedca.AzureProvisioner - (*ACMEProvisioner)(nil), // 13: linkedca.ACMEProvisioner - (*X5CProvisioner)(nil), // 14: linkedca.X5CProvisioner - (*K8SSAProvisioner)(nil), // 15: linkedca.K8sSAProvisioner - (*SSHPOPProvisioner)(nil), // 16: linkedca.SSHPOPProvisioner - (*SCEPProvisioner)(nil), // 17: linkedca.SCEPProvisioner -} -var file_linkedca_provisioners_proto_depIdxs = []int32{ - 0, // 0: linkedca.Provisioner.type:type_name -> linkedca.Provisioner.Type - 2, // 1: linkedca.Provisioner.details:type_name -> linkedca.ProvisionerDetails - 4, // 2: linkedca.Provisioner.claims:type_name -> linkedca.Claims - 8, // 3: linkedca.ProvisionerDetails.JWK:type_name -> linkedca.JWKProvisioner - 9, // 4: linkedca.ProvisionerDetails.OIDC:type_name -> linkedca.OIDCProvisioner - 10, // 5: linkedca.ProvisionerDetails.GCP:type_name -> linkedca.GCPProvisioner - 11, // 6: linkedca.ProvisionerDetails.AWS:type_name -> linkedca.AWSProvisioner - 12, // 7: linkedca.ProvisionerDetails.Azure:type_name -> linkedca.AzureProvisioner - 13, // 8: linkedca.ProvisionerDetails.ACME:type_name -> linkedca.ACMEProvisioner - 14, // 9: linkedca.ProvisionerDetails.X5C:type_name -> linkedca.X5CProvisioner - 15, // 10: linkedca.ProvisionerDetails.K8sSA:type_name -> linkedca.K8sSAProvisioner - 16, // 11: linkedca.ProvisionerDetails.SSHPOP:type_name -> linkedca.SSHPOPProvisioner - 17, // 12: linkedca.ProvisionerDetails.SCEP:type_name -> linkedca.SCEPProvisioner - 1, // 13: linkedca.ProvisionerList.provisioners:type_name -> linkedca.Provisioner - 5, // 14: linkedca.Claims.x509:type_name -> linkedca.X509Claims - 6, // 15: linkedca.Claims.ssh:type_name -> linkedca.SSHClaims - 7, // 16: linkedca.X509Claims.durations:type_name -> linkedca.Durations - 7, // 17: linkedca.SSHClaims.user_durations:type_name -> linkedca.Durations - 7, // 18: linkedca.SSHClaims.host_durations:type_name -> linkedca.Durations - 19, // [19:19] is the sub-list for method output_type - 19, // [19:19] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name -} - -func init() { file_linkedca_provisioners_proto_init() } -func file_linkedca_provisioners_proto_init() { - if File_linkedca_provisioners_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_linkedca_provisioners_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Provisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProvisionerDetails); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ProvisionerList); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Claims); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*X509Claims); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHClaims); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Durations); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*JWKProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OIDCProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCPProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AWSProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AzureProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ACMEProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*X5CProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*K8SSAProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SSHPOPProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_linkedca_provisioners_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SCEPProvisioner); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_linkedca_provisioners_proto_msgTypes[1].OneofWrappers = []interface{}{ - (*ProvisionerDetails_JWK)(nil), - (*ProvisionerDetails_OIDC)(nil), - (*ProvisionerDetails_GCP)(nil), - (*ProvisionerDetails_AWS)(nil), - (*ProvisionerDetails_Azure)(nil), - (*ProvisionerDetails_ACME)(nil), - (*ProvisionerDetails_X5C)(nil), - (*ProvisionerDetails_K8SSA)(nil), - (*ProvisionerDetails_SSHPOP)(nil), - (*ProvisionerDetails_SCEP)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_linkedca_provisioners_proto_rawDesc, - NumEnums: 1, - NumMessages: 17, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_linkedca_provisioners_proto_goTypes, - DependencyIndexes: file_linkedca_provisioners_proto_depIdxs, - EnumInfos: file_linkedca_provisioners_proto_enumTypes, - MessageInfos: file_linkedca_provisioners_proto_msgTypes, - }.Build() - File_linkedca_provisioners_proto = out.File - file_linkedca_provisioners_proto_rawDesc = nil - file_linkedca_provisioners_proto_goTypes = nil - file_linkedca_provisioners_proto_depIdxs = nil -} diff --git a/linkedca/provisioners.proto b/linkedca/provisioners.proto deleted file mode 100644 index ae047391..00000000 --- a/linkedca/provisioners.proto +++ /dev/null @@ -1,133 +0,0 @@ -syntax = "proto3"; - -package linkedca; - -option go_package = "github.com/smallstep/certificates/linkedca"; - -message Provisioner { - enum Type { - NOOP = 0; - JWK = 1; - OIDC = 2; - GCP = 3; - AWS = 4; - AZURE = 5; - ACME = 6; - X5C = 7; - K8SSA = 8; - SSHPOP = 9; - SCEP = 10; - } - string id = 1; - string authority_id = 2; - Type type = 3; - string name = 4; - ProvisionerDetails details = 5; - Claims claims = 6; - bytes x509_template = 7; - bytes x509_template_data = 8; - bytes ssh_template = 9; - bytes ssh_template_data = 10; -} - -message ProvisionerDetails { - oneof data { - JWKProvisioner JWK = 20; - OIDCProvisioner OIDC = 21; - GCPProvisioner GCP = 22; - AWSProvisioner AWS = 23; - AzureProvisioner Azure = 24; - ACMEProvisioner ACME = 25; - X5CProvisioner X5C = 26; - K8sSAProvisioner K8sSA = 27; - SSHPOPProvisioner SSHPOP = 28; - SCEPProvisioner SCEP = 29; - } -} - -message ProvisionerList { - repeated Provisioner provisioners = 1; -} - -message Claims { - X509Claims x509 = 1; - SSHClaims ssh = 2; - bool disable_renewal = 3; -} - -message X509Claims { - bool enabled = 1; - Durations durations = 2; -} - -message SSHClaims { - bool enabled = 1; - Durations user_durations = 2; - Durations host_durations = 3; -} - -message Durations { - string default = 1; - string min = 2; - string max = 3; -} - -message JWKProvisioner { - bytes public_key = 1; - bytes encrypted_private_key = 2; -} - -message OIDCProvisioner { - string client_id = 1; - string client_secret = 2; - string configuration_endpoint = 3; - repeated string admins = 4; - repeated string domains = 5; - repeated string groups = 6; - string listen_address = 7; - string tenant_id = 8; -} - -message GCPProvisioner { - repeated string service_accounts = 1; - repeated string project_ids = 2; - bool disable_custom_sans = 3; - bool disable_trust_on_first_use = 4; - string instance_age = 5; -} - -message AWSProvisioner { - repeated string accounts = 1; - bool disable_custom_sans = 2; - bool disable_trust_on_first_use = 3; - string instance_age = 4; -} - -message AzureProvisioner { - string tenant_id = 1; - repeated string resource_groups = 2; - string audience = 3; - bool disable_custom_sans = 4; - bool disable_trust_on_first_use = 5; -} - -message ACMEProvisioner { - bool force_cn = 1; -} - -message X5CProvisioner { - repeated bytes roots = 1; -} - -message K8sSAProvisioner { - repeated bytes public_keys = 1; -} - -message SSHPOPProvisioner {} - -message SCEPProvisioner { - bool force_cn = 1; - string challenge = 2; - repeated string capabilities = 3; - int32 minimum_public_key_length = 4; -} \ No newline at end of file diff --git a/scep/api/api.go b/scep/api/api.go index e64eef83..7036ffcb 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -67,8 +67,8 @@ func New(scepAuth scep.Interface) api.RouterHandler { // Route traffic and implement the Router interface. func (h *Handler) Route(r api.Router) { getLink := h.Auth.GetLinkExplicit - r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) - r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) + r.MethodFunc(http.MethodGet, getLink("{name}", false, nil), h.lookupProvisioner(h.Get)) + r.MethodFunc(http.MethodPost, getLink("{name}", false, nil), h.lookupProvisioner(h.Post)) } // Get handles all SCEP GET requests @@ -185,14 +185,14 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { - name := chi.URLParam(r, "provisionerID") - provisionerID, err := url.PathUnescape(name) + nameEscaped := chi.URLParam(r, "name") + name, err := url.PathUnescape(nameEscaped) if err != nil { api.WriteError(w, errors.Errorf("error url unescaping provisioner id '%s'", name)) return } - p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) + p, err := h.Auth.LoadProvisionerByName(name) if err != nil { api.WriteError(w, err) return diff --git a/scep/authority.go b/scep/authority.go index 47d1d41c..5c863b63 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -20,7 +20,7 @@ import ( // Interface is the SCEP authority interface. type Interface interface { - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string GetCACertificates() ([]*x509.Certificate, error) @@ -56,7 +56,7 @@ type AuthorityOptions struct { // SignAuthority is the interface for a signing authority type SignAuthority interface { Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) - LoadProvisionerByID(string) (provisioner.Interface, error) + LoadProvisionerByName(string) (provisioner.Interface, error) } // New returns a new Authority that implements the SCEP interface. @@ -92,10 +92,10 @@ var ( } ) -// LoadProvisionerByID calls out to the SignAuthority interface to load a -// provisioner by ID. -func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { - return a.signAuth.LoadProvisionerByID(id) +// LoadProvisionerByName calls out to the SignAuthority interface to load a +// provisioner by name. +func (a *Authority) LoadProvisionerByName(name string) (provisioner.Interface, error) { + return a.signAuth.LoadProvisionerByName(name) } // GetLinkExplicit returns the requested link from the directory.