diff --git a/go.mod b/go.mod index 3f25ba98..9b5e069b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/rond-authz/rond go 1.18 require ( - github.com/davidebianchi/go-jsonclient v1.5.0 github.com/davidebianchi/gswagger v0.9.0 github.com/getkin/kin-openapi v0.118.0 github.com/google/uuid v1.3.0 @@ -28,6 +27,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davidebianchi/go-jsonclient v1.5.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-ini/ini v1.67.0 // indirect diff --git a/internal/crudclient/crudclient.go b/internal/crudclient/crudclient.go deleted file mode 100644 index 01fc2582..00000000 --- a/internal/crudclient/crudclient.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2021 Mia srl -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crudclient - -import ( - "context" - "fmt" - "net/http" - "strings" - - "github.com/davidebianchi/go-jsonclient" - "github.com/rond-authz/rond/helpers" -) - -// CRUD struct. -type CRUD struct { - httpClient *jsonclient.Client -} - -// New creates new CRUDClient. -func New(apiURL string) (*CRUD, error) { - apiURLWithoutFinalSlash := strings.TrimSuffix(apiURL, "/") - - opts := jsonclient.Options{ - BaseURL: fmt.Sprintf("%s/", apiURLWithoutFinalSlash), - } - httpClient, err := jsonclient.New(opts) - if err != nil { - return nil, err - } - - crudClient := &CRUD{ - httpClient, - } - return crudClient, nil -} - -// Get fetch item by id on CRUD. -func (crud CRUD) Get(ctx context.Context, queryParam string, responseBody interface{}) error { - req, err := crud.httpClient.NewRequestWithContext(ctx, http.MethodGet, "?"+queryParam, nil) - if err != nil { - return err - } - - helpers.SetHeadersToProxy(ctx, req.Header) - - if _, err := crud.httpClient.Do(req, responseBody); err != nil { - return err - } - return nil -} - -func (crud CRUD) Post(ctx context.Context, body interface{}, responseBody interface{}) error { - req, err := crud.httpClient.NewRequestWithContext(ctx, http.MethodPost, "", body) - if err != nil { - return err - } - - helpers.SetHeadersToProxy(ctx, req.Header) - - _, err = crud.httpClient.Do(req, responseBody) - if err != nil { - return err - } - - return nil -} - -func (crud CRUD) Delete(ctx context.Context, queryParam string, responseBody interface{}) error { - req, err := crud.httpClient.NewRequestWithContext(ctx, http.MethodDelete, "?"+queryParam, nil) - if err != nil { - return err - } - - helpers.SetHeadersToProxy(ctx, req.Header) - - if _, err := crud.httpClient.Do(req, responseBody); err != nil { - return err - } - return nil -} - -func (crud CRUD) PatchBulk(ctx context.Context, requestBody interface{}, responseBody interface{}) error { - req, err := crud.httpClient.NewRequestWithContext(ctx, http.MethodPatch, "", requestBody) - if err != nil { - return err - } - - helpers.SetHeadersToProxy(ctx, req.Header) - - if _, err := crud.httpClient.Do(req, responseBody); err != nil { - return err - } - return nil -} - -// IsHealthy checks if crud is healthy. -func (crud CRUD) IsHealthy(ctx context.Context) error { - req, err := crud.httpClient.NewRequest(http.MethodGet, "/-/healthz", nil) - if err != nil { - return err - } - - helpers.SetHeadersToProxy(ctx, req.Header) - - if _, err := crud.httpClient.Do(req, nil); err != nil { - return err - } - return nil -} diff --git a/internal/crudclient/crudclient_test.go b/internal/crudclient/crudclient_test.go deleted file mode 100644 index 05aa81c3..00000000 --- a/internal/crudclient/crudclient_test.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2021 Mia srl -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crudclient - -import ( - "context" - "net/http" - "testing" - - "github.com/rond-authz/rond/helpers" - "github.com/stretchr/testify/require" -) - -func TestCRUDClient(t *testing.T) { - usersCrudBaseURL := "http://crud.example.org/my-coll-name" - - t.Run("New", func(t *testing.T) { - t.Run("should return a Client instance", func(t *testing.T) { - crudClient, err := New(usersCrudBaseURL) - require.NoError(t, err) - require.NotNil(t, crudClient) - }) - - t.Run("https is allowed", func(t *testing.T) { - crudClient, err := New("https://crud.example.org") - require.NoError(t, err) - require.NotNil(t, crudClient) - }) - - t.Run("should return error invalid url", func(t *testing.T) { - crudClient, err := New("in\t") - require.Error(t, err) - require.Nil(t, crudClient) - }) - - t.Run("should return error on unknown protocol", func(t *testing.T) { - crudClient, err := New("in://validURL") - require.Error(t, err) - require.Nil(t, crudClient) - }) - - t.Run("should return error if URL is not absolute - 1", func(t *testing.T) { - crudClient, err := New("validURL") - require.Error(t, err) - require.Nil(t, crudClient) - }) - - t.Run("should return error if URL is not absolute - 2", func(t *testing.T) { - crudClient, err := New("/validURL") - require.Error(t, err) - require.Nil(t, crudClient) - }) - }) -} - -func TestGet(t *testing.T) { - usersCrudBaseURL := "http://crud.example.org/my-coll-name/" - - crudClient, err := New(usersCrudBaseURL) - require.NoError(t, err) - require.NotNil(t, crudClient) - var ctx = context.Background() - - type ResponseBody struct { - Bar string `json:"bar"` - } - - t.Run("returns error creating request", func(t *testing.T) { - err := crudClient.Get(ctx, " ", nil) - require.Error(t, err) - }) - - t.Run("returns error if context timeout", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, 0) - defer cancel() - - err := crudClient.Get(ctx, "", nil) - require.EqualError(t, err, context.DeadlineExceeded.Error()) - }) - - t.Run("returns error if crud returns 404", func(t *testing.T) { - MockGet(t, usersCrudBaseURL, 404, nil, nil) - - err := crudClient.Get(ctx, "", nil) - require.EqualError(t, err, "GET http://crud.example.org/my-coll-name/?: 404 - null\n") - }) - - t.Run("correctly returns if crud returns 200", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - MockGet(t, usersCrudBaseURL, 200, expectedResponseBody, nil) - - var responseBody ResponseBody - err := crudClient.Get(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) - - t.Run("correctly returns if crud returns 200 with additional headers to proxy", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - headersToProxy := http.Header{} - headersToProxy.Set("request-id", "123") - headersToProxy.Set("taz", "ok") - - ctx = helpers.AddHeadersToProxyToContext(ctx, headersToProxy) - - MockGet(t, usersCrudBaseURL, 200, expectedResponseBody, headersToProxy) - - var responseBody ResponseBody - err := crudClient.Get(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) -} - -func TestPost(t *testing.T) { - usersCrudBaseURL := "http://crud.example.org/my-coll-name/" - - crudClient, err := New(usersCrudBaseURL) - require.NoError(t, err) - require.NotNil(t, crudClient) - var ctx = context.Background() - - type ResponseBody struct { - Bar string `json:"bar"` - } - - t.Run("returns error creating request", func(t *testing.T) { - err := crudClient.Post(ctx, nil, nil) - require.Error(t, err) - }) - - t.Run("returns error if context timeout", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, 0) - defer cancel() - - err := crudClient.Post(ctx, "", nil) - require.EqualError(t, err, context.DeadlineExceeded.Error()) - }) - - t.Run("returns error if crud returns 404", func(t *testing.T) { - MockPost(t, usersCrudBaseURL, 404, nil, nil) - - err := crudClient.Post(ctx, "", nil) - require.EqualError(t, err, "POST http://crud.example.org/my-coll-name/: 404 - null\n") - }) - - t.Run("correctly returns if crud returns 200", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - MockPost(t, usersCrudBaseURL, 200, expectedResponseBody, nil) - - var responseBody ResponseBody - err := crudClient.Get(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) - - t.Run("correctly returns if crud returns 200 with additional headers to proxy", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - headersToProxy := http.Header{} - headersToProxy.Set("request-id", "123") - headersToProxy.Set("taz", "ok") - - ctx = helpers.AddHeadersToProxyToContext(ctx, headersToProxy) - - MockPost(t, usersCrudBaseURL, 200, expectedResponseBody, headersToProxy) - - var responseBody ResponseBody - err := crudClient.Get(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) -} - -func TestDelete(t *testing.T) { - usersCrudBaseURL := "http://crud.example.org/my-coll-name/" - - crudClient, err := New(usersCrudBaseURL) - require.NoError(t, err) - require.NotNil(t, crudClient) - var ctx = context.Background() - - type ResponseBody struct { - Bar string `json:"bar"` - } - - t.Run("returns error creating request", func(t *testing.T) { - err := crudClient.Delete(ctx, "", nil) - require.Error(t, err) - }) - - t.Run("returns error if context timeout", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, 0) - defer cancel() - - err := crudClient.Delete(ctx, "", nil) - require.EqualError(t, err, context.DeadlineExceeded.Error()) - }) - - t.Run("returns error if crud returns 404", func(t *testing.T) { - MockDelete(t, usersCrudBaseURL, 404, nil, nil) - - err := crudClient.Delete(ctx, "", nil) - require.EqualError(t, err, "DELETE http://crud.example.org/my-coll-name/?: 404 - null\n") - }) - - t.Run("correctly returns if crud returns 200", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - MockDelete(t, usersCrudBaseURL, 200, expectedResponseBody, nil) - - var responseBody ResponseBody - err := crudClient.Delete(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) - - t.Run("correctly returns if crud returns 200 with additional headers to proxy", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - headersToProxy := http.Header{} - headersToProxy.Set("request-id", "123") - headersToProxy.Set("taz", "ok") - - ctx = helpers.AddHeadersToProxyToContext(ctx, headersToProxy) - - MockDelete(t, usersCrudBaseURL, 200, expectedResponseBody, headersToProxy) - - var responseBody ResponseBody - err := crudClient.Delete(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) -} - -func TestPatchBulk(t *testing.T) { - usersCrudBaseURL := "http://crud.example.org/my-coll-name/" - - crudClient, err := New(usersCrudBaseURL) - require.NoError(t, err) - require.NotNil(t, crudClient) - var ctx = context.Background() - - type ResponseBody struct { - Bar string `json:"bar"` - } - - t.Run("returns error creating request", func(t *testing.T) { - err := crudClient.PatchBulk(ctx, nil, nil) - require.Error(t, err) - }) - - t.Run("returns error if context timeout", func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, 0) - defer cancel() - - err := crudClient.PatchBulk(ctx, "", nil) - require.EqualError(t, err, context.DeadlineExceeded.Error()) - }) - - t.Run("returns error if crud returns 404", func(t *testing.T) { - MockPatchBulk(t, usersCrudBaseURL, 404, nil, nil) - - err := crudClient.PatchBulk(ctx, "", nil) - require.EqualError(t, err, "PATCH http://crud.example.org/my-coll-name/: 404 - null\n") - }) - - t.Run("correctly returns if crud returns 200", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - MockPatchBulk(t, usersCrudBaseURL, 200, expectedResponseBody, nil) - - var responseBody ResponseBody - err := crudClient.PatchBulk(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) - - t.Run("correctly returns if crud returns 200 with additional headers to proxy", func(t *testing.T) { - expectedResponseBody := ResponseBody{ - Bar: "some", - } - - headersToProxy := http.Header{} - headersToProxy.Set("request-id", "123") - headersToProxy.Set("taz", "ok") - - ctx = helpers.AddHeadersToProxyToContext(ctx, headersToProxy) - - MockPatchBulk(t, usersCrudBaseURL, 200, expectedResponseBody, headersToProxy) - - var responseBody ResponseBody - err := crudClient.PatchBulk(ctx, "", &responseBody) - - require.NoError(t, err) - require.Equal(t, expectedResponseBody, responseBody) - }) -} - -func TestIsHealthy(t *testing.T) { - usersCrudBaseURL := "http://crud.example.org/my-coll-name/" - - crudClient, err := New(usersCrudBaseURL) - require.NoError(t, err) - require.NotNil(t, crudClient) - - t.Run("ok", func(t *testing.T) { - MockIsHealthy(t, usersCrudBaseURL, 200, nil) - - err := crudClient.IsHealthy(context.Background()) - require.NoError(t, err) - }) - - t.Run("ok - proxy headers", func(t *testing.T) { - h := http.Header{} - h.Set("foo", "bar") - MockIsHealthy(t, usersCrudBaseURL, 200, h) - - ctx := context.Background() - ctx = helpers.AddHeadersToProxyToContext(ctx, h) - - err := crudClient.IsHealthy(ctx) - require.NoError(t, err) - }) - - t.Run("ko", func(t *testing.T) { - MockIsHealthy(t, usersCrudBaseURL, 503, nil) - - err := crudClient.IsHealthy(context.Background()) - require.Error(t, err) - }) -} diff --git a/internal/crudclient/testing.go b/internal/crudclient/testing.go deleted file mode 100644 index b35ddc92..00000000 --- a/internal/crudclient/testing.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2021 Mia srl -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crudclient - -import ( - "net/http" - "testing" - - "gopkg.in/h2non/gock.v1" -) - -// MockUpsertWithQueryParameters mocks upsert to a collection. -func getHeadersMap(headers http.Header) map[string]string { - requestHeadersMap := map[string]string{} - for name, values := range headers { - requestHeadersMap[name] = values[0] - } - return requestHeadersMap -} - -// MockGetByID mocks get in a collection. -func MockGet(t *testing.T, baseURL string, statusCode int, responseBody interface{}, headersToProxy http.Header) { - t.Helper() - t.Cleanup(func() { - gockCleanup(t) - }) - gock.DisableNetworking() - - gock.New(baseURL). - MatchHeaders(getHeadersMap(headersToProxy)). - Reply(statusCode). - JSON(responseBody) -} - -// MockPost mocks post in a collection. -func MockPost(t *testing.T, baseURL string, statusCode int, responseBody interface{}, headersToProxy http.Header) { - t.Helper() - t.Cleanup(func() { - gockCleanup(t) - }) - gock.DisableNetworking() - - gock.New(baseURL). - MatchHeaders(getHeadersMap(headersToProxy)). - Reply(statusCode). - JSON(responseBody) -} - -// MockDelete mocks post in a collection. -func MockDelete(t *testing.T, baseURL string, statusCode int, responseBody interface{}, headersToProxy http.Header) { - t.Helper() - t.Cleanup(func() { - gockCleanup(t) - }) - gock.DisableNetworking() - - gock.New(baseURL). - MatchHeaders(getHeadersMap(headersToProxy)). - Reply(statusCode). - JSON(responseBody) -} - -// MockPatchBulk mocks post in a collection. -func MockPatchBulk(t *testing.T, baseURL string, statusCode int, responseBody interface{}, headersToProxy http.Header) { - t.Helper() - t.Cleanup(func() { - gockCleanup(t) - }) - gock.DisableNetworking() - - gock.New(baseURL). - MatchHeaders(getHeadersMap(headersToProxy)). - Reply(statusCode). - JSON(responseBody) -} - -// MockIsHealthy mock the healthy function -func MockIsHealthy(t *testing.T, baseURL string, statusCode int, headersToProxy http.Header) { - t.Helper() - t.Cleanup(func() { - gockCleanup(t) - }) - gock.DisableNetworking() - - responseBody := map[string]interface{}{ - "status": "OK", - } - if statusCode >= 300 { - responseBody = map[string]interface{}{ - "status": "KO", - } - } - - gock.New(baseURL). - MatchHeaders(getHeadersMap(headersToProxy)). - Get("/-/healthz"). - Reply(statusCode). - JSON(responseBody) -} - -func gockCleanup(t *testing.T) { - t.Helper() - - if !gock.IsDone() { - gock.OffAll() - t.Fatal("fails to mock crud") - } - gock.Off() -} diff --git a/service/standalone_apis.go b/service/standalone_apis.go index f900219f..ac1c2cc9 100644 --- a/service/standalone_apis.go +++ b/service/standalone_apis.go @@ -21,7 +21,6 @@ import ( "github.com/rond-authz/rond/helpers" "github.com/rond-authz/rond/internal/config" - "github.com/rond-authz/rond/internal/crudclient" "github.com/rond-authz/rond/internal/utils" "github.com/rond-authz/rond/types" @@ -191,7 +190,10 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { return } - client, err := crudclient.New(env.BindingsCrudServiceURL) + client, err := crud.NewClient[types.Binding](crud.ClientOptions{ + BaseURL: env.BindingsCrudServiceURL, + Headers: helpers.GetHeadersToProxy(r, env.GetAdditionalHeadersToProxy()), + }) if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud setup") utils.FailResponseWithCode(w, http.StatusInternalServerError, err.Error(), utils.GENERIC_BUSINESS_ERROR_MESSAGE) @@ -213,14 +215,14 @@ func grantHandler(w http.ResponseWriter, r *http.Request) { } } - var bindingIDCreated types.BindingCreateResponse - if err := client.Post(r.Context(), &bindingToCreate, &bindingIDCreated); err != nil { + bindingIDCreated, err := client.Create(r.Context(), bindingToCreate, crud.Options{}) + if err != nil { logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("failed crud request") utils.FailResponseWithCode(w, http.StatusInternalServerError, "failed crud request for creating bindings", utils.GENERIC_BUSINESS_ERROR_MESSAGE) return } logger.WithFields(logrus.Fields{ - "createdBindingObjectId": utils.SanitizeString(bindingIDCreated.ObjectID), + "createdBindingObjectId": utils.SanitizeString(bindingIDCreated), "createdBindingId": utils.SanitizeString(bindingToCreate.BindingID), "resourceId": utils.SanitizeString(reqBody.ResourceID), "resourceType": utils.SanitizeString(resourceType),