diff --git a/api/api.go b/api/api.go index 912e39dd..47d2fd27 100644 --- a/api/api.go +++ b/api/api.go @@ -20,6 +20,7 @@ import ( "github.com/go-chi/chi" "github.com/pkg/errors" + "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" diff --git a/api/api_test.go b/api/api_test.go index 717621cd..25abdeff 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -28,15 +28,17 @@ import ( "github.com/go-chi/chi" "github.com/pkg/errors" + "golang.org/x/crypto/ssh" + + "go.step.sm/crypto/jose" + "go.step.sm/crypto/x509util" + "github.com/smallstep/assert" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/templates" - "go.step.sm/crypto/jose" - "go.step.sm/crypto/x509util" - "golang.org/x/crypto/ssh" ) const ( diff --git a/api/errors.go b/api/errors.go index bff46b55..522fa955 100644 --- a/api/errors.go +++ b/api/errors.go @@ -7,6 +7,7 @@ import ( "os" "github.com/pkg/errors" + "github.com/smallstep/certificates/acme" "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/errs" diff --git a/api/read/read.go b/api/read/read.go new file mode 100644 index 00000000..de92c5d7 --- /dev/null +++ b/api/read/read.go @@ -0,0 +1,31 @@ +// Package read implements request object readers. +package read + +import ( + "encoding/json" + "io" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + + "github.com/smallstep/certificates/errs" +) + +// JSON reads JSON from the request body and stores it in the value +// pointed by v. +func JSON(r io.Reader, v interface{}) error { + if err := json.NewDecoder(r).Decode(v); err != nil { + return errs.BadRequestErr(err, "error decoding json") + } + return nil +} + +// ProtoJSON reads JSON from the request body and stores it in the value +// pointed by v. +func ProtoJSON(r io.Reader, m proto.Message) error { + data, err := io.ReadAll(r) + if err != nil { + return errs.BadRequestErr(err, "error reading request body") + } + return protojson.Unmarshal(data, m) +} diff --git a/api/read/read_test.go b/api/read/read_test.go new file mode 100644 index 00000000..f2eff1bc --- /dev/null +++ b/api/read/read_test.go @@ -0,0 +1,46 @@ +package read + +import ( + "io" + "reflect" + "strings" + "testing" + + "github.com/smallstep/certificates/errs" +) + +func TestJSON(t *testing.T) { + type args struct { + r io.Reader + v interface{} + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"ok", args{strings.NewReader(`{"foo":"bar"}`), make(map[string]interface{})}, false}, + {"fail", args{strings.NewReader(`{"foo"}`), make(map[string]interface{})}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := JSON(tt.args.r, &tt.args.v) + if (err != nil) != tt.wantErr { + t.Errorf("JSON() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.wantErr { + e, ok := err.(*errs.Error) + if ok { + if code := e.StatusCode(); code != 400 { + t.Errorf("error.StatusCode() = %v, wants 400", code) + } + } else { + t.Errorf("error type = %T, wants *Error", err) + } + } else if !reflect.DeepEqual(tt.args.v, map[string]interface{}{"foo": "bar"}) { + t.Errorf("JSON value = %v, wants %v", tt.args.v, map[string]interface{}{"foo": "bar"}) + } + }) + } +} diff --git a/api/rekey.go b/api/rekey.go index b7958844..269086bb 100644 --- a/api/rekey.go +++ b/api/rekey.go @@ -3,6 +3,7 @@ package api import ( "net/http" + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/errs" ) @@ -32,7 +33,7 @@ func (h *caHandler) Rekey(w http.ResponseWriter, r *http.Request) { } var body RekeyRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/revoke.go b/api/revoke.go index 25520e3e..49822e6d 100644 --- a/api/revoke.go +++ b/api/revoke.go @@ -4,11 +4,13 @@ import ( "context" "net/http" + "golang.org/x/crypto/ocsp" + + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" - "golang.org/x/crypto/ocsp" ) // RevokeResponse is the response object that returns the health of the server. @@ -48,7 +50,7 @@ func (r *RevokeRequest) Validate() (err error) { // TODO: Add CRL and OCSP support. func (h *caHandler) Revoke(w http.ResponseWriter, r *http.Request) { var body RevokeRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/revoke_test.go b/api/revoke_test.go index 4ed4e3fe..7635ce68 100644 --- a/api/revoke_test.go +++ b/api/revoke_test.go @@ -13,6 +13,7 @@ import ( "testing" "github.com/pkg/errors" + "github.com/smallstep/assert" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" diff --git a/api/sign.go b/api/sign.go index 93c5f599..b2eef45d 100644 --- a/api/sign.go +++ b/api/sign.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" @@ -49,7 +50,7 @@ type SignResponse struct { // information in the certificate request. func (h *caHandler) Sign(w http.ResponseWriter, r *http.Request) { var body SignRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/ssh.go b/api/ssh.go index c9be1527..fc185d07 100644 --- a/api/ssh.go +++ b/api/ssh.go @@ -9,12 +9,14 @@ import ( "time" "github.com/pkg/errors" + "golang.org/x/crypto/ssh" + + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/config" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/templates" - "golang.org/x/crypto/ssh" ) // SSHAuthority is the interface implemented by a SSH CA authority. @@ -249,7 +251,7 @@ type SSHBastionResponse struct { // the request. func (h *caHandler) SSHSign(w http.ResponseWriter, r *http.Request) { var body SSHSignRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } @@ -393,7 +395,7 @@ func (h *caHandler) SSHFederation(w http.ResponseWriter, r *http.Request) { // and servers. func (h *caHandler) SSHConfig(w http.ResponseWriter, r *http.Request) { var body SSHConfigRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } @@ -425,7 +427,7 @@ func (h *caHandler) SSHConfig(w http.ResponseWriter, r *http.Request) { // SSHCheckHost is the HTTP handler that returns if a hosts certificate exists or not. func (h *caHandler) SSHCheckHost(w http.ResponseWriter, r *http.Request) { var body SSHCheckPrincipalRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } @@ -464,7 +466,7 @@ func (h *caHandler) SSHGetHosts(w http.ResponseWriter, r *http.Request) { // SSHBastion provides returns the bastion configured if any. func (h *caHandler) SSHBastion(w http.ResponseWriter, r *http.Request) { var body SSHBastionRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/sshRekey.go b/api/sshRekey.go index 8670f0bd..b7581749 100644 --- a/api/sshRekey.go +++ b/api/sshRekey.go @@ -4,9 +4,11 @@ import ( "net/http" "time" + "golang.org/x/crypto/ssh" + + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "golang.org/x/crypto/ssh" ) // SSHRekeyRequest is the request body of an SSH certificate request. @@ -38,7 +40,7 @@ type SSHRekeyResponse struct { // the request. func (h *caHandler) SSHRekey(w http.ResponseWriter, r *http.Request) { var body SSHRekeyRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/sshRenew.go b/api/sshRenew.go index 57b6f432..b98466bf 100644 --- a/api/sshRenew.go +++ b/api/sshRenew.go @@ -6,6 +6,8 @@ import ( "time" "github.com/pkg/errors" + + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" ) @@ -36,7 +38,7 @@ type SSHRenewResponse struct { // the request. func (h *caHandler) SSHRenew(w http.ResponseWriter, r *http.Request) { var body SSHRenewRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/sshRevoke.go b/api/sshRevoke.go index 60f44f2a..2d2da1f7 100644 --- a/api/sshRevoke.go +++ b/api/sshRevoke.go @@ -3,11 +3,13 @@ package api import ( "net/http" + "golang.org/x/crypto/ocsp" + + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/logging" - "golang.org/x/crypto/ocsp" ) // SSHRevokeResponse is the response object that returns the health of the server. @@ -47,7 +49,7 @@ func (r *SSHRevokeRequest) Validate() (err error) { // NOTE: currently only Passive revocation is supported. func (h *caHandler) SSHRevoke(w http.ResponseWriter, r *http.Request) { var body SSHRevokeRequest - if err := ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { WriteError(w, errs.BadRequestErr(err, "error reading request body")) return } diff --git a/api/ssh_test.go b/api/ssh_test.go index a3d7da0d..88a301f5 100644 --- a/api/ssh_test.go +++ b/api/ssh_test.go @@ -18,12 +18,13 @@ import ( "testing" "time" + "golang.org/x/crypto/ssh" + "github.com/smallstep/assert" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/logging" "github.com/smallstep/certificates/templates" - "golang.org/x/crypto/ssh" ) var ( diff --git a/api/utils.go b/api/utils.go index a7f4bf58..9daa0cd2 100644 --- a/api/utils.go +++ b/api/utils.go @@ -2,14 +2,13 @@ package api import ( "encoding/json" - "io" "log" "net/http" - "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/logging" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" + + "github.com/smallstep/certificates/logging" ) // EnableLogger is an interface that enables response logging for an object. @@ -88,22 +87,3 @@ func ProtoJSONStatus(w http.ResponseWriter, m proto.Message, status int) { } //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 { - if err := json.NewDecoder(r).Decode(v); err != nil { - return errs.BadRequestErr(err, "error decoding json") - } - 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 := io.ReadAll(r) - if err != nil { - return errs.BadRequestErr(err, "error reading request body") - } - return protojson.Unmarshal(data, m) -} diff --git a/api/utils_test.go b/api/utils_test.go index 81146653..12350c97 100644 --- a/api/utils_test.go +++ b/api/utils_test.go @@ -1,15 +1,13 @@ package api import ( - "io" "net/http" "net/http/httptest" "reflect" - "strings" "testing" "github.com/pkg/errors" - "github.com/smallstep/certificates/errs" + "github.com/smallstep/certificates/logging" ) @@ -88,38 +86,3 @@ func TestJSON(t *testing.T) { }) } } - -func TestReadJSON(t *testing.T) { - type args struct { - r io.Reader - v interface{} - } - tests := []struct { - name string - args args - wantErr bool - }{ - {"ok", args{strings.NewReader(`{"foo":"bar"}`), make(map[string]interface{})}, false}, - {"fail", args{strings.NewReader(`{"foo"}`), make(map[string]interface{})}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := ReadJSON(tt.args.r, &tt.args.v) - if (err != nil) != tt.wantErr { - t.Errorf("ReadJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if tt.wantErr { - e, ok := err.(*errs.Error) - if ok { - if code := e.StatusCode(); code != 400 { - t.Errorf("error.StatusCode() = %v, wants 400", code) - } - } else { - t.Errorf("error type = %T, wants *Error", err) - } - } else if !reflect.DeepEqual(tt.args.v, map[string]interface{}{"foo": "bar"}) { - t.Errorf("ReadJSON value = %v, wants %v", tt.args.v, map[string]interface{}{"foo": "bar"}) - } - }) - } -} diff --git a/authority/admin/api/admin.go b/authority/admin/api/admin.go index 7aa66d0f..43607c52 100644 --- a/authority/admin/api/admin.go +++ b/authority/admin/api/admin.go @@ -5,10 +5,13 @@ import ( "net/http" "github.com/go-chi/chi" + + "go.step.sm/linkedca" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority/admin" "github.com/smallstep/certificates/authority/provisioner" - "go.step.sm/linkedca" ) type adminAuthority interface { @@ -112,7 +115,7 @@ 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) { var body CreateAdminRequest - if err := api.ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body")) return } @@ -156,7 +159,7 @@ func (h *Handler) DeleteAdmin(w http.ResponseWriter, r *http.Request) { // UpdateAdmin updates an existing admin. func (h *Handler) UpdateAdmin(w http.ResponseWriter, r *http.Request) { var body UpdateAdminRequest - if err := api.ReadJSON(r.Body, &body); err != nil { + if err := read.JSON(r.Body, &body); err != nil { api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err, "error reading request body")) return } diff --git a/authority/admin/api/provisioner.go b/authority/admin/api/provisioner.go index b8cc0f4c..2106733d 100644 --- a/authority/admin/api/provisioner.go +++ b/authority/admin/api/provisioner.go @@ -4,12 +4,14 @@ import ( "net/http" "github.com/go-chi/chi" + "go.step.sm/linkedca" + "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/api/read" "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. @@ -72,7 +74,7 @@ func (h *Handler) GetProvisioners(w http.ResponseWriter, r *http.Request) { // 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 { + if err := read.ProtoJSON(r.Body, prov); err != nil { api.WriteError(w, err) return } @@ -122,7 +124,7 @@ func (h *Handler) DeleteProvisioner(w http.ResponseWriter, r *http.Request) { // 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 { + if err := read.ProtoJSON(r.Body, nu); err != nil { api.WriteError(w, err) return } diff --git a/ca/client_test.go b/ca/client_test.go index a00ca1cf..4628d19b 100644 --- a/ca/client_test.go +++ b/ca/client_test.go @@ -17,13 +17,16 @@ import ( "time" "github.com/pkg/errors" + "golang.org/x/crypto/ssh" + + "go.step.sm/crypto/x509util" + "github.com/smallstep/assert" "github.com/smallstep/certificates/api" + "github.com/smallstep/certificates/api/read" "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/errs" - "go.step.sm/crypto/x509util" - "golang.org/x/crypto/ssh" ) const ( @@ -354,7 +357,7 @@ func TestClient_Sign(t *testing.T) { srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { body := new(api.SignRequest) - if err := api.ReadJSON(req.Body, body); err != nil { + if err := read.JSON(req.Body, body); err != nil { e, ok := tt.response.(error) assert.Fatal(t, ok, "response expected to be error type") api.WriteError(w, e) @@ -426,7 +429,7 @@ func TestClient_Revoke(t *testing.T) { srv.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { body := new(api.RevokeRequest) - if err := api.ReadJSON(req.Body, body); err != nil { + if err := read.JSON(req.Body, body); err != nil { e, ok := tt.response.(error) assert.Fatal(t, ok, "response expected to be error type") api.WriteError(w, e)